Orion PHP  0.11.12
The PHP5.3 framework
security.php
Go to the documentation of this file.
00001 <?php
00002 
00003 namespace Orion\Core;
00004 
00005 
00006 /**
00007  * \Orion\Core\Security
00008  * 
00009  * Orion security class.
00010  * 
00011  * Contains security-related methods, like password generator,
00012  * Injection escape, hashing, validation, etc.
00013  *
00014  * @author Thibaut Despoulain
00015  * @license BSD 4-clauses
00016  * @version 0.11.12
00017  *
00018  * @static
00019  */
00020 class Security
00021 {
00022     const E_INVALID_JSON = 81;
00023     const E_INVALID_EXT = 82;
00024     const E_CSRF_FAIL = 83;
00025     const E_HTMLAWED_FAIL = 84;
00026 
00027     /**
00028      * Check CSRF token validity. Throws a Security\Exception if a CSRF attack is detected.
00029      * @param String $key The token identifier used in csrfGenerate, also the key of the token inside $origin
00030      * @param Mixed $origin The origin of the token to test (mostly $_POST or $_GET), but can also be a custom associative array. This array must contain the token under the key $key.
00031      * @param Boolean $forceExit Set this to TRUE to force the script to exit(1) if the CSRF check fails.
00032      */
00033     public static function csrfCheck( $key, $origin, $forceExit=false )
00034     {
00035         try
00036         {
00037             if ( !isset( $_SESSION[ 'csrf_' . $key ] ) )
00038                 throw new \Exception( 'Missing CSRF session token.' );
00039 
00040             if ( !isset( $origin[ $key ] ) )
00041                 throw new \Exception( 'Missing CSRF form token.' );
00042 
00043             // Get valid token from session
00044             $hash = $_SESSION[ 'csrf_' . $key ];
00045             // Free up session token for one-time CSRF token usage.
00046             $_SESSION[ 'csrf_' . $key ] = null;
00047 
00048             // Check if session token matches form token
00049             if ( $origin[ $key ] != $hash )
00050                 throw new \Exception( 'Invalid CSRF token.' );
00051 
00052             // Check for token expiration
00053             if ( $timespan != null && is_int( $timespan ) && intval( substr( base64_decode( $hash ), 0, 10 ) ) + $timespan < time() )
00054                 throw new \Exception( 'CSRF token has expired.' );
00055         }
00056         catch ( \Exception $e )
00057         {
00058             throw new Security\Exception( 'CSRF Check failed ! Please use the original form to send data.', self::E_CSRF_FAIL, $forceExit );
00059         }
00060     }
00061 
00062     /**
00063      * Generates a new anti-CSRF token and stores it in session for future check.
00064      * @param String $key The token identifier
00065      * @return Hash The token
00066      */
00067     public static function csrfGenerate( $key )
00068     {
00069         // token generation (basically base64_encode any random complex string, time() is used for token expiration) 
00070         $token = base64_encode( time() . self::genPassword( 32 ) );
00071         // store the one-time token in session
00072         $_SESSION[ 'csrf_' . $key ] = $token;
00073 
00074         return $token;
00075     }
00076 
00077     /**
00078      * Generates a random alphanumeric password
00079      * @param Integer $length Password length
00080      * @param String $custom String containing custom chars
00081      * @return string 
00082      */
00083     public static function genPassword( $length, $custom='' )
00084     {
00085         $seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijqlmnopqrtsuvwxyz0123456789' . $custom;
00086         $max = strlen( $seed ) - 1;
00087 
00088         $password = '';
00089         for ( $i = 0; $i < $length; ++$i )
00090         {
00091             $password .= $seed{intval( mt_rand( 0.0, $max ) )};
00092         }
00093         return $password;
00094     }
00095 
00096     /**
00097      * Process given string using the htmLawed algorithm. Deny risky HTML content.
00098      * @see <http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/>
00099      * @param String $input The text to process
00100      * @param Boolean $safemode Use builtin 'safe' configuration
00101      * @param Mixed $config Custom configuration array (extends default configuration)
00102      * @return String The processed text
00103      */
00104     public static function htmLawed( $input, $safemode=true, $config=null )
00105     {
00106         $localConfig = array( );
00107 
00108         if ( $safemode )
00109         {
00110             $localConfig[ 'safe' ] = 1;
00111             $localConfig[ 'deny_attribute' ] = 'style';
00112         }
00113         if ( $config != null && is_array( $config ) )
00114             array_merge( $localConfig, $config );
00115 
00116         try
00117         {
00118             include_once( Context::getLibsPath( 'htmLawed.php' ) );
00119             return htmLawed( $input, $localConfig );
00120         }
00121         catch ( \Exception $e )
00122         {
00123             if ( \Orion::isDebug() )
00124                 throw $e;
00125             else
00126                 throw new Security\Exception( 'An exception occured while trying to run security parsing using htmLawed.', self::E_HTMLAWED_FAIL );
00127         }
00128     }
00129 
00130     /**
00131      * Escapes a string to be put into htML to prevent SQL/JS injections
00132      * @param string $string
00133      * @return string
00134      */
00135     public static function preventInjection( $string )
00136     {
00137         return htmlspecialchars( $string );
00138     }
00139 
00140     /*
00141      * hash('md5', $data) shortcut
00142      */
00143 
00144     public static function md5Hash( $data )
00145     {
00146         if ( empty( $data ) )
00147             throw new Exception( 'Unable to hash provided string. String is empty.' );
00148         return hash( 'md5', $data );
00149     }
00150 
00151     /**
00152      * An elaborated split/double-salted hash method to hash passwords for example.
00153      * Uses sha1 as final hashing algorithm
00154      * @param string $data
00155      * @param string $extrasalt
00156      * @return hash
00157      */
00158     public static function saltedHash( $data, $extrasalt )
00159     {
00160         $password = str_split( $data, (strlen( $data ) / 2) + 1 );
00161         $hash = hash( 'sha1', $extrasalt . $password[ 0 ] . \Orion::config()->get( 'SECURITY_KEY' ) . $password[ 1 ] );
00162         return $hash;
00163     }
00164 
00165     /**
00166      * Removes risky parts from a standard file path (., .., empty) and normalise directory separators
00167      * @param string $path
00168      * @return string
00169      */
00170     public static function sanitizePath( $path )
00171     {
00172         $path = str_replace( array( '\\', '/' ), array( DS, DS ), $path );
00173         $p = explode( DS, $path );
00174         $out = $path{0} == DS ? array( '' ) : array( );
00175         foreach ( $p as $dir )
00176         {
00177             if ( $dir == '' || $dir == '.' || $dir == '..' )
00178                 continue;
00179 
00180             $out[ ] = $dir;
00181         }
00182         return implode( DS, $out );
00183     }
00184 
00185     /**
00186      * Test if given filename uses on of the given extentions.
00187      * @param string $string The file name
00188      * @param string|string[] $ext The extention(s) (without the .)
00189      * @return boolean
00190      */
00191     public static function validateExtension( $string, $ext )
00192     {
00193         if ( is_string( $ext ) )
00194             $ext = array( $ext );
00195 
00196         return (preg_match( '/\.(?:' . implode( '|', $ext ) . ')$/six', $string ) > 0);
00197     }
00198 
00199     /**
00200      * Validates a JSON string.
00201      * @param string $data The json-encoded data
00202      * @return boolean
00203      */
00204     public static function validateJSON( $data )
00205     {
00206         $jsonregex = '/
00207                       (?(DEFINE)
00208                          (?<number>   -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )    
00209                          (?<boolean>   true | false | null )
00210                          (?<string>    " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )
00211                          (?<array>     \[  (?:  (?&json)  (?: , (?&json)  )*  )?  \s* \] )
00212                          (?<pair>      \s* (?&string) \s* : (?&json)  )
00213                          (?<object>    \{  (?:  (?&pair)  (?: , (?&pair)  )*  )?  \s* \} )
00214                          (?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )
00215                       )
00216                       \A (?&json) \Z
00217                       /six';
00218         return (preg_match( $jsonregex, $data ) > 0);
00219     }
00220 
00221 }
00222 
00223 ?>