pictcode / lib / Cake / Utility / Security.php @ 2df93201
履歴 | 表示 | アノテート | ダウンロード (11.271 KB)
| 1 | 635eef61 | spyder1211 | <?php
 | 
      
|---|---|---|---|
| 2 | /**
 | 
      ||
| 3 |  * Core Security
 | 
      ||
| 4 |  *
 | 
      ||
| 5 |  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 | 
      ||
| 6 |  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 | 
      ||
| 7 |  *
 | 
      ||
| 8 |  * Licensed under The MIT License
 | 
      ||
| 9 |  * For full copyright and license information, please see the LICENSE.txt
 | 
      ||
| 10 |  * Redistributions of files must retain the above copyright notice.
 | 
      ||
| 11 |  *
 | 
      ||
| 12 |  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 | 
      ||
| 13 |  * @link          http://cakephp.org CakePHP(tm) Project
 | 
      ||
| 14 |  * @package       Cake.Utility
 | 
      ||
| 15 |  * @since         CakePHP(tm) v .0.10.0.1233
 | 
      ||
| 16 |  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 | 
      ||
| 17 |  */
 | 
      ||
| 18 | |||
| 19 | App::uses('CakeText', 'Utility');  | 
      ||
| 20 | |||
| 21 | /**
 | 
      ||
| 22 |  * Security Library contains utility methods related to security
 | 
      ||
| 23 |  *
 | 
      ||
| 24 |  * @package       Cake.Utility
 | 
      ||
| 25 |  */
 | 
      ||
| 26 | class Security {  | 
      ||
| 27 | |||
| 28 | /**
 | 
      ||
| 29 |  * Default hash method
 | 
      ||
| 30 |  *
 | 
      ||
| 31 |  * @var string
 | 
      ||
| 32 |  */
 | 
      ||
| 33 | public static $hashType = null;  | 
      ||
| 34 | |||
| 35 | /**
 | 
      ||
| 36 |  * Default cost
 | 
      ||
| 37 |  *
 | 
      ||
| 38 |  * @var string
 | 
      ||
| 39 |  */
 | 
      ||
| 40 | public static $hashCost = '10';  | 
      ||
| 41 | |||
| 42 | /**
 | 
      ||
| 43 |  * Get allowed minutes of inactivity based on security level.
 | 
      ||
| 44 |  *
 | 
      ||
| 45 |  * @deprecated 3.0.0 Exists for backwards compatibility only, not used by the core
 | 
      ||
| 46 |  * @return int Allowed inactivity in minutes
 | 
      ||
| 47 |  */
 | 
      ||
| 48 | public static function inactiveMins() {  | 
      ||
| 49 | switch (Configure::read('Security.level')) {  | 
      ||
| 50 | case 'high':  | 
      ||
| 51 | return 10;  | 
      ||
| 52 | case 'medium':  | 
      ||
| 53 | return 100;  | 
      ||
| 54 | case 'low':  | 
      ||
| 55 |                         default:
 | 
      ||
| 56 | return 300;  | 
      ||
| 57 | }  | 
      ||
| 58 | }  | 
      ||
| 59 | |||
| 60 | /**
 | 
      ||
| 61 |  * Generate authorization hash.
 | 
      ||
| 62 |  *
 | 
      ||
| 63 |  * @return string Hash
 | 
      ||
| 64 |  */
 | 
      ||
| 65 | public static function generateAuthKey() {  | 
      ||
| 66 | return Security::hash(CakeText::uuid());  | 
      ||
| 67 | }  | 
      ||
| 68 | |||
| 69 | /**
 | 
      ||
| 70 |  * Validate authorization hash.
 | 
      ||
| 71 |  *
 | 
      ||
| 72 |  * @param string $authKey Authorization hash
 | 
      ||
| 73 |  * @return bool Success
 | 
      ||
| 74 |  */
 | 
      ||
| 75 | public static function validateAuthKey($authKey) {  | 
      ||
| 76 | return true;  | 
      ||
| 77 | }  | 
      ||
| 78 | |||
| 79 | /**
 | 
      ||
| 80 |  * Create a hash from string using given method or fallback on next available method.
 | 
      ||
| 81 |  *
 | 
      ||
| 82 |  * #### Using Blowfish
 | 
      ||
| 83 |  *
 | 
      ||
| 84 |  * - Creating Hashes: *Do not supply a salt*. Cake handles salt creation for
 | 
      ||
| 85 |  * you ensuring that each hashed password will have a *unique* salt.
 | 
      ||
| 86 |  * - Comparing Hashes: Simply pass the originally hashed password as the salt.
 | 
      ||
| 87 |  * The salt is prepended to the hash and php handles the parsing automagically.
 | 
      ||
| 88 |  * For convenience the `BlowfishPasswordHasher` class is available for use with
 | 
      ||
| 89 |  * the AuthComponent.
 | 
      ||
| 90 |  * - Do NOT use a constant salt for blowfish!
 | 
      ||
| 91 |  *
 | 
      ||
| 92 |  * Creating a blowfish/bcrypt hash:
 | 
      ||
| 93 |  *
 | 
      ||
| 94 |  * ```
 | 
      ||
| 95 |  *         $hash = Security::hash($password, 'blowfish');
 | 
      ||
| 96 |  * ```
 | 
      ||
| 97 |  *
 | 
      ||
| 98 |  * @param string $string String to hash
 | 
      ||
| 99 |  * @param string $type Method to use (sha1/sha256/md5/blowfish)
 | 
      ||
| 100 |  * @param mixed $salt If true, automatically prepends the application's salt
 | 
      ||
| 101 |  *     value to $string (Security.salt). If you are using blowfish the salt
 | 
      ||
| 102 |  *     must be false or a previously generated salt.
 | 
      ||
| 103 |  * @return string Hash
 | 
      ||
| 104 |  * @link http://book.cakephp.org/2.0/en/core-utility-libraries/security.html#Security::hash
 | 
      ||
| 105 |  */
 | 
      ||
| 106 | public static function hash($string, $type = null, $salt = false) {  | 
      ||
| 107 | if (empty($type)) {  | 
      ||
| 108 | $type = static::$hashType;  | 
      ||
| 109 | }  | 
      ||
| 110 | $type = strtolower($type);  | 
      ||
| 111 | |||
| 112 | if ($type === 'blowfish') {  | 
      ||
| 113 | return static::_crypt($string, $salt);  | 
      ||
| 114 | }  | 
      ||
| 115 | if ($salt) {  | 
      ||
| 116 | if (!is_string($salt)) {  | 
      ||
| 117 | $salt = Configure::read('Security.salt');  | 
      ||
| 118 | }  | 
      ||
| 119 | $string = $salt . $string;  | 
      ||
| 120 | }  | 
      ||
| 121 | |||
| 122 | if (!$type || $type === 'sha1') {  | 
      ||
| 123 | if (function_exists('sha1')) {  | 
      ||
| 124 | return sha1($string);  | 
      ||
| 125 | }  | 
      ||
| 126 | $type = 'sha256';  | 
      ||
| 127 | }  | 
      ||
| 128 | |||
| 129 | if ($type === 'sha256' && function_exists('mhash')) {  | 
      ||
| 130 | return bin2hex(mhash(MHASH_SHA256, $string));  | 
      ||
| 131 | }  | 
      ||
| 132 | |||
| 133 | if (function_exists('hash')) {  | 
      ||
| 134 | return hash($type, $string);  | 
      ||
| 135 | }  | 
      ||
| 136 | return md5($string);  | 
      ||
| 137 | }  | 
      ||
| 138 | |||
| 139 | /**
 | 
      ||
| 140 |  * Sets the default hash method for the Security object. This affects all objects using
 | 
      ||
| 141 |  * Security::hash().
 | 
      ||
| 142 |  *
 | 
      ||
| 143 |  * @param string $hash Method to use (sha1/sha256/md5/blowfish)
 | 
      ||
| 144 |  * @return void
 | 
      ||
| 145 |  * @see Security::hash()
 | 
      ||
| 146 |  */
 | 
      ||
| 147 | public static function setHash($hash) {  | 
      ||
| 148 | static::$hashType = $hash;  | 
      ||
| 149 | }  | 
      ||
| 150 | |||
| 151 | /**
 | 
      ||
| 152 |  * Sets the cost for they blowfish hash method.
 | 
      ||
| 153 |  *
 | 
      ||
| 154 |  * @param int $cost Valid values are 4-31
 | 
      ||
| 155 |  * @return void
 | 
      ||
| 156 |  */
 | 
      ||
| 157 | public static function setCost($cost) {  | 
      ||
| 158 | if ($cost < 4 || $cost > 31) {  | 
      ||
| 159 |                         trigger_error(__d(
 | 
      ||
| 160 |                                 'cake_dev',
 | 
      ||
| 161 |                                 'Invalid value, cost must be between %s and %s',
 | 
      ||
| 162 | array(4, 31)  | 
      ||
| 163 |                         ), E_USER_WARNING);
 | 
      ||
| 164 | return null;  | 
      ||
| 165 | }  | 
      ||
| 166 | static::$hashCost = $cost;  | 
      ||
| 167 | }  | 
      ||
| 168 | |||
| 169 | /**
 | 
      ||
| 170 |  * Runs $text through a XOR cipher.
 | 
      ||
| 171 |  *
 | 
      ||
| 172 |  * *Note* This is not a cryptographically strong method and should not be used
 | 
      ||
| 173 |  * for sensitive data. Additionally this method does *not* work in environments
 | 
      ||
| 174 |  * where suhosin is enabled.
 | 
      ||
| 175 |  *
 | 
      ||
| 176 |  * Instead you should use Security::rijndael() when you need strong
 | 
      ||
| 177 |  * encryption.
 | 
      ||
| 178 |  *
 | 
      ||
| 179 |  * @param string $text Encrypted string to decrypt, normal string to encrypt
 | 
      ||
| 180 |  * @param string $key Key to use
 | 
      ||
| 181 |  * @return string Encrypted/Decrypted string
 | 
      ||
| 182 |  * @deprecated 3.0.0 Will be removed in 3.0.
 | 
      ||
| 183 |  */
 | 
      ||
| 184 | public static function cipher($text, $key) {  | 
      ||
| 185 | if (empty($key)) {  | 
      ||
| 186 | trigger_error(__d('cake_dev', 'You cannot use an empty key for %s', 'Security::cipher()'), E_USER_WARNING);  | 
      ||
| 187 | return '';  | 
      ||
| 188 | }  | 
      ||
| 189 | |||
| 190 | srand(Configure::read('Security.cipherSeed'));  | 
      ||
| 191 | $out = '';  | 
      ||
| 192 | $keyLength = strlen($key);  | 
      ||
| 193 | for ($i = 0, $textLength = strlen($text); $i < $textLength; $i++) {  | 
      ||
| 194 | $j = ord(substr($key, $i % $keyLength, 1));  | 
      ||
| 195 | while ($j--) {  | 
      ||
| 196 | rand(0, 255);  | 
      ||
| 197 | }  | 
      ||
| 198 | $mask = rand(0, 255);  | 
      ||
| 199 | $out .= chr(ord(substr($text, $i, 1)) ^ $mask);  | 
      ||
| 200 | }  | 
      ||
| 201 |                 srand();
 | 
      ||
| 202 | return $out;  | 
      ||
| 203 | }  | 
      ||
| 204 | |||
| 205 | /**
 | 
      ||
| 206 |  * Encrypts/Decrypts a text using the given key using rijndael method.
 | 
      ||
| 207 |  *
 | 
      ||
| 208 |  * Prior to 2.3.1, a fixed initialization vector was used. This was not
 | 
      ||
| 209 |  * secure. This method now uses a random iv, and will silently upgrade values when
 | 
      ||
| 210 |  * they are re-encrypted.
 | 
      ||
| 211 |  *
 | 
      ||
| 212 |  * @param string $text Encrypted string to decrypt, normal string to encrypt
 | 
      ||
| 213 |  * @param string $key Key to use as the encryption key for encrypted data.
 | 
      ||
| 214 |  * @param string $operation Operation to perform, encrypt or decrypt
 | 
      ||
| 215 |  * @return string Encrypted/Decrypted string
 | 
      ||
| 216 |  */
 | 
      ||
| 217 | public static function rijndael($text, $key, $operation) {  | 
      ||
| 218 | if (empty($key)) {  | 
      ||
| 219 | trigger_error(__d('cake_dev', 'You cannot use an empty key for %s', 'Security::rijndael()'), E_USER_WARNING);  | 
      ||
| 220 | return '';  | 
      ||
| 221 | }  | 
      ||
| 222 | if (empty($operation) || !in_array($operation, array('encrypt', 'decrypt'))) {  | 
      ||
| 223 | trigger_error(__d('cake_dev', 'You must specify the operation for Security::rijndael(), either encrypt or decrypt'), E_USER_WARNING);  | 
      ||
| 224 | return '';  | 
      ||
| 225 | }  | 
      ||
| 226 | if (strlen($key) < 32) {  | 
      ||
| 227 | trigger_error(__d('cake_dev', 'You must use a key larger than 32 bytes for Security::rijndael()'), E_USER_WARNING);  | 
      ||
| 228 | return '';  | 
      ||
| 229 | }  | 
      ||
| 230 | $algorithm = MCRYPT_RIJNDAEL_256;  | 
      ||
| 231 | $mode = MCRYPT_MODE_CBC;  | 
      ||
| 232 | $ivSize = mcrypt_get_iv_size($algorithm, $mode);  | 
      ||
| 233 | |||
| 234 | $cryptKey = substr($key, 0, 32);  | 
      ||
| 235 | |||
| 236 | if ($operation === 'encrypt') {  | 
      ||
| 237 | $iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);  | 
      ||
| 238 | return $iv . '$$' . mcrypt_encrypt($algorithm, $cryptKey, $text, $mode, $iv);  | 
      ||
| 239 | }  | 
      ||
| 240 |                 // Backwards compatible decrypt with fixed iv
 | 
      ||
| 241 | if (substr($text, $ivSize, 2) !== '$$') {  | 
      ||
| 242 | $iv = substr($key, strlen($key) - 32, 32);  | 
      ||
| 243 | return rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");  | 
      ||
| 244 | }  | 
      ||
| 245 | $iv = substr($text, 0, $ivSize);  | 
      ||
| 246 | $text = substr($text, $ivSize + 2);  | 
      ||
| 247 | return rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");  | 
      ||
| 248 | }  | 
      ||
| 249 | |||
| 250 | /**
 | 
      ||
| 251 |  * Generates a pseudo random salt suitable for use with php's crypt() function.
 | 
      ||
| 252 |  * The salt length should not exceed 27. The salt will be composed of
 | 
      ||
| 253 |  * [./0-9A-Za-z]{$length}.
 | 
      ||
| 254 |  *
 | 
      ||
| 255 |  * @param int $length The length of the returned salt
 | 
      ||
| 256 |  * @return string The generated salt
 | 
      ||
| 257 |  */
 | 
      ||
| 258 | protected static function _salt($length = 22) {  | 
      ||
| 259 | $salt = str_replace(  | 
      ||
| 260 | array('+', '='),  | 
      ||
| 261 |                         '.',
 | 
      ||
| 262 | base64_encode(sha1(uniqid(Configure::read('Security.salt'), true), true))  | 
      ||
| 263 | );  | 
      ||
| 264 | return substr($salt, 0, $length);  | 
      ||
| 265 | }  | 
      ||
| 266 | |||
| 267 | /**
 | 
      ||
| 268 |  * One way encryption using php's crypt() function. To use blowfish hashing see ``Security::hash()``
 | 
      ||
| 269 |  *
 | 
      ||
| 270 |  * @param string $password The string to be encrypted.
 | 
      ||
| 271 |  * @param mixed $salt false to generate a new salt or an existing salt.
 | 
      ||
| 272 |  * @return string The hashed string or an empty string on error.
 | 
      ||
| 273 |  */
 | 
      ||
| 274 | protected static function _crypt($password, $salt = false) {  | 
      ||
| 275 | if ($salt === false) {  | 
      ||
| 276 | $salt = static::_salt(22);  | 
      ||
| 277 | $salt = vsprintf('$2a$%02d$%s', array(static::$hashCost, $salt));  | 
      ||
| 278 | }  | 
      ||
| 279 | |||
| 280 |                 $invalidCipher = (
 | 
      ||
| 281 | strpos($salt, '$2y$') !== 0 &&  | 
      ||
| 282 | strpos($salt, '$2x$') !== 0 &&  | 
      ||
| 283 | strpos($salt, '$2a$') !== 0  | 
      ||
| 284 | );  | 
      ||
| 285 | if ($salt === true || $invalidCipher || strlen($salt) < 29) {  | 
      ||
| 286 |                         trigger_error(__d(
 | 
      ||
| 287 |                                 'cake_dev',
 | 
      ||
| 288 |                                 'Invalid salt: %s for %s Please visit http://www.php.net/crypt and read the appropriate section for building %s salts.',
 | 
      ||
| 289 | array($salt, 'blowfish', 'blowfish')  | 
      ||
| 290 |                         ), E_USER_WARNING);
 | 
      ||
| 291 | return '';  | 
      ||
| 292 | }  | 
      ||
| 293 | return crypt($password, $salt);  | 
      ||
| 294 | }  | 
      ||
| 295 | |||
| 296 | /**
 | 
      ||
| 297 |  * Encrypt a value using AES-256.
 | 
      ||
| 298 |  *
 | 
      ||
| 299 |  * *Caveat* You cannot properly encrypt/decrypt data with trailing null bytes.
 | 
      ||
| 300 |  * Any trailing null bytes will be removed on decryption due to how PHP pads messages
 | 
      ||
| 301 |  * with nulls prior to encryption.
 | 
      ||
| 302 |  *
 | 
      ||
| 303 |  * @param string $plain The value to encrypt.
 | 
      ||
| 304 |  * @param string $key The 256 bit/32 byte key to use as a cipher key.
 | 
      ||
| 305 |  * @param string $hmacSalt The salt to use for the HMAC process. Leave null to use Security.salt.
 | 
      ||
| 306 |  * @return string Encrypted data.
 | 
      ||
| 307 |  * @throws CakeException On invalid data or key.
 | 
      ||
| 308 |  */
 | 
      ||
| 309 | public static function encrypt($plain, $key, $hmacSalt = null) {  | 
      ||
| 310 | static::_checkKey($key, 'encrypt()');  | 
      ||
| 311 | |||
| 312 | if ($hmacSalt === null) {  | 
      ||
| 313 | $hmacSalt = Configure::read('Security.salt');  | 
      ||
| 314 | }  | 
      ||
| 315 | |||
| 316 |                 // Generate the encryption and hmac key.
 | 
      ||
| 317 | $key = substr(hash('sha256', $key . $hmacSalt), 0, 32);  | 
      ||
| 318 | |||
| 319 | $algorithm = MCRYPT_RIJNDAEL_128;  | 
      ||
| 320 | $mode = MCRYPT_MODE_CBC;  | 
      ||
| 321 | |||
| 322 | $ivSize = mcrypt_get_iv_size($algorithm, $mode);  | 
      ||
| 323 | $iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);  | 
      ||
| 324 | $ciphertext = $iv . mcrypt_encrypt($algorithm, $key, $plain, $mode, $iv);  | 
      ||
| 325 | $hmac = hash_hmac('sha256', $ciphertext, $key);  | 
      ||
| 326 | return $hmac . $ciphertext;  | 
      ||
| 327 | }  | 
      ||
| 328 | |||
| 329 | /**
 | 
      ||
| 330 |  * Check the encryption key for proper length.
 | 
      ||
| 331 |  *
 | 
      ||
| 332 |  * @param string $key Key to check.
 | 
      ||
| 333 |  * @param string $method The method the key is being checked for.
 | 
      ||
| 334 |  * @return void
 | 
      ||
| 335 |  * @throws CakeException When key length is not 256 bit/32 bytes
 | 
      ||
| 336 |  */
 | 
      ||
| 337 | protected static function _checkKey($key, $method) {  | 
      ||
| 338 | if (strlen($key) < 32) {  | 
      ||
| 339 | throw new CakeException(__d('cake_dev', 'Invalid key for %s, key must be at least 256 bits (32 bytes) long.', $method));  | 
      ||
| 340 | }  | 
      ||
| 341 | }  | 
      ||
| 342 | |||
| 343 | /**
 | 
      ||
| 344 |  * Decrypt a value using AES-256.
 | 
      ||
| 345 |  *
 | 
      ||
| 346 |  * @param string $cipher The ciphertext to decrypt.
 | 
      ||
| 347 |  * @param string $key The 256 bit/32 byte key to use as a cipher key.
 | 
      ||
| 348 |  * @param string $hmacSalt The salt to use for the HMAC process. Leave null to use Security.salt.
 | 
      ||
| 349 |  * @return string Decrypted data. Any trailing null bytes will be removed.
 | 
      ||
| 350 |  * @throws CakeException On invalid data or key.
 | 
      ||
| 351 |  */
 | 
      ||
| 352 | public static function decrypt($cipher, $key, $hmacSalt = null) {  | 
      ||
| 353 | static::_checkKey($key, 'decrypt()');  | 
      ||
| 354 | if (empty($cipher)) {  | 
      ||
| 355 | throw new CakeException(__d('cake_dev', 'The data to decrypt cannot be empty.'));  | 
      ||
| 356 | }  | 
      ||
| 357 | if ($hmacSalt === null) {  | 
      ||
| 358 | $hmacSalt = Configure::read('Security.salt');  | 
      ||
| 359 | }  | 
      ||
| 360 | |||
| 361 |                 // Generate the encryption and hmac key.
 | 
      ||
| 362 | $key = substr(hash('sha256', $key . $hmacSalt), 0, 32);  | 
      ||
| 363 | |||
| 364 |                 // Split out hmac for comparison
 | 
      ||
| 365 | $macSize = 64;  | 
      ||
| 366 | $hmac = substr($cipher, 0, $macSize);  | 
      ||
| 367 | $cipher = substr($cipher, $macSize);  | 
      ||
| 368 | |||
| 369 | $compareHmac = hash_hmac('sha256', $cipher, $key);  | 
      ||
| 370 | if ($hmac !== $compareHmac) {  | 
      ||
| 371 | return false;  | 
      ||
| 372 | }  | 
      ||
| 373 | |||
| 374 | $algorithm = MCRYPT_RIJNDAEL_128;  | 
      ||
| 375 | $mode = MCRYPT_MODE_CBC;  | 
      ||
| 376 | $ivSize = mcrypt_get_iv_size($algorithm, $mode);  | 
      ||
| 377 | |||
| 378 | $iv = substr($cipher, 0, $ivSize);  | 
      ||
| 379 | $cipher = substr($cipher, $ivSize);  | 
      ||
| 380 | $plain = mcrypt_decrypt($algorithm, $key, $cipher, $mode, $iv);  | 
      ||
| 381 | return rtrim($plain, "\0");  | 
      ||
| 382 | }  | 
      ||
| 383 | |||
| 384 | }  |