PageRenderTime 37ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/lithium/security/Password.php

https://bitbucket.org/thesyncim/front-biarq
PHP | 240 lines | 71 code | 22 blank | 147 comment | 15 complexity | ce9b7ea6716a9a8d9a7ec8eb20f33edf MD5 | raw file
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2011, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\security;
  9. use lithium\util\String;
  10. /**
  11. * `Password` utility class that makes use of PHP's `crypt()` function. Includes a
  12. * cryptographically strong salt generator, and utility functions to hash and check
  13. * passwords.
  14. */
  15. class Password {
  16. /**
  17. * The default log2 number of iterations for Blowfish encryption.
  18. */
  19. const BF = 10;
  20. /**
  21. * The default log2 number of iterations for XDES encryption.
  22. */
  23. const XDES = 18;
  24. /**
  25. * Hashes a password using PHP's `crypt()` and an optional salt. If no
  26. * salt is supplied, a cryptographically strong salt will be generated
  27. * using `lithium\security\Password::salt()`.
  28. *
  29. * Using this function is the proper way to hash a password. Using naive
  30. * methods such as sha1 or md5, as is done in many web applications, is
  31. * improper due to the lack of a cryptographically strong salt.
  32. *
  33. * Using `lithium\security\Password::hash()` ensures that:
  34. *
  35. * - Two identical passwords will never use the same salt, thus never
  36. * resulting in the same hash; this prevents a potential attacker from
  37. * compromising user accounts by using a database of most commonly used
  38. * passwords.
  39. * - The salt generator's count iterator can be increased within Lithium
  40. * or your application as computer hardware becomes faster; this results
  41. * in slower hash generation, without invalidating existing passwords.
  42. *
  43. * Usage:
  44. *
  45. * {{{
  46. * // Hash a password before storing it:
  47. * $hashed = Password::hash($password);
  48. *
  49. * // Check a password by comparing it to its hashed value:
  50. * $check = Password::check($password, $hashed);
  51. *
  52. * // Use a stronger custom salt:
  53. * $salt = Password::salt('bf', 16); // 2^16 iterations
  54. * $hashed = Password::hash($password, $salt); // Very slow
  55. * $check = Password::check($password, $hashed); // Very slow
  56. *
  57. * // Forward/backward compatibility
  58. * $salt1 = Password::salt('bf', 6);
  59. * $salt2 = Password::salt('bf', 12);
  60. * $hashed1 = Password::hash($password, $salt1); // Fast
  61. * $hashed2 = Password::hash($password, $salt2); // Slow
  62. * $check1 = Password::check($password, $hashed1); // True
  63. * $check2 = Password::check($password, $hashed2); // True
  64. * }}}
  65. *
  66. * @param string $password The password to hash.
  67. * @param string $salt Optional. The salt string.
  68. * @return string The hashed password.
  69. * The result's length will be:
  70. * - 60 chars long for Blowfish hashes
  71. * - 20 chars long for XDES hashes
  72. * - 34 chars long for MD5 hashes
  73. * @see lithium\security\Password::check()
  74. * @see lithium\security\Password::salt()
  75. */
  76. public static function hash($password, $salt = null) {
  77. return crypt($password, $salt ?: static::salt());
  78. }
  79. /**
  80. * Compares a password and its hashed value using PHP's `crypt()`. Rather than a simple string
  81. * comparison, this method uses a constant-time algorithm to defend against
  82. * [timing attacks](http://codahale.com/a-lesson-in-timing-attacks/).
  83. *
  84. * @param string $password The password to check.
  85. * @param string $hash The hashed password to compare it to.
  86. * @return boolean Returns a boolean indicating whether the password is correct.
  87. * @see lithium\security\Password::hash()
  88. * @see lithium\security\Password::salt()
  89. */
  90. public static function check($password, $hash) {
  91. $password = crypt($password, $hash);
  92. $result = true;
  93. if (($length = strlen($password)) != strlen($hash)) {
  94. return false;
  95. }
  96. for ($i = 0; $i < $length; $i++) {
  97. $result = $result && ($password[$i] === $hash[$i]);
  98. }
  99. return $result;
  100. }
  101. /**
  102. * Generates a cryptographically strong salt, using the best available
  103. * method (tries Blowfish, then XDES, and fallbacks to MD5), for use in
  104. * `Password::hash()`.
  105. *
  106. * Blowfish and XDES are adaptive hashing algorithms. MD5 is not. Adaptive
  107. * hashing algorithms are designed in such a way that when computers get
  108. * faster, you can tune the algorithm to be slower by increasing the number
  109. * of hash iterations, without introducing incompatibility with existing
  110. * passwords.
  111. *
  112. * To pick an appropriate iteration count for adaptive algorithms, consider
  113. * that the original DES crypt was designed to have the speed of 4 hashes
  114. * per second on the hardware of that time. Slower than 4 hashes per second
  115. * would probably dampen usability. Faster than 100 hashes per second is
  116. * probably too fast. The defaults generate about 10 hashes per second
  117. * using a dual-core 2.2GHz CPU.
  118. *
  119. * _Note 1_: this salt generator is different from naive salt implementations
  120. * (e.g. `md5(microtime())`) in that it uses all of the available bits of
  121. * entropy for the supplied salt method.
  122. *
  123. * _Note2_: this method should not be use to generate custom salts. Indeed,
  124. * the resulting salts are prefixed with information expected by PHP's
  125. * `crypt()`. To get an arbitrarily long, cryptographically strong salt
  126. * consisting in random sequences of alpha numeric characters, use
  127. * `lithium\util\String::random()` instead.
  128. *
  129. * @param string $type The hash type. Optional. Defaults to the best
  130. * available option. Supported values, along with their maximum
  131. * password lengths, include:
  132. * - `'bf'`: Blowfish (128 salt bits, max 72 chars)
  133. * - `'xdes'`: XDES (24 salt bits, max 8 chars)
  134. * - `'md5'`: MD5 (48 salt bits, unlimited length)
  135. * @param integer $count Optional. The base-2 logarithm of the iteration
  136. * count, for adaptive algorithms. Defaults to:
  137. * - `10` for Blowfish
  138. * - `18` for XDES
  139. * @return string The salt string.
  140. * @link http://php.net/manual/en/function.crypt.php
  141. * @link http://www.postgresql.org/docs/9.0/static/pgcrypto.html
  142. * @see lithium\security\Password::hash()
  143. * @see lithium\security\Password::check()
  144. * @see lithium\util\String::random()
  145. */
  146. public static function salt($type = null, $count = null) {
  147. switch (true) {
  148. case CRYPT_BLOWFISH == 1 && (!$type || $type === 'bf'):
  149. return static::_genSaltBf($count);
  150. case CRYPT_EXT_DES == 1 && (!$type || $type === 'xdes'):
  151. return static::_genSaltXDES($count);
  152. default:
  153. return static::_genSaltMD5();
  154. }
  155. }
  156. /**
  157. * Generates a Blowfish salt for use in `lithium\security\Password::hash()`. _Note_: Does not
  158. * use the `'encode'` option of `String::random()` because it could result in 2 bits less of
  159. * entropy depending on the last character.
  160. *
  161. * @param integer $count The base-2 logarithm of the iteration count.
  162. * Defaults to `10`. Can be `4` to `31`.
  163. * @return string The Blowfish salt.
  164. */
  165. protected static function _genSaltBf($count = 10) {
  166. $count = (integer) $count;
  167. $count = ($count < 4 || $count > 31) ? 10 : $count;
  168. $base64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  169. $i = 0;
  170. $input = String::random(16);
  171. $output = '';
  172. do {
  173. $c1 = ord($input[$i++]);
  174. $output .= $base64[$c1 >> 2];
  175. $c1 = ($c1 & 0x03) << 4;
  176. if ($i >= 16) {
  177. $output .= $base64[$c1];
  178. break;
  179. }
  180. $c2 = ord($input[$i++]);
  181. $c1 |= $c2 >> 4;
  182. $output .= $base64[$c1];
  183. $c1 = ($c2 & 0x0f) << 2;
  184. $c2 = ord($input[$i++]);
  185. $c1 |= $c2 >> 6;
  186. $output .= $base64[$c1];
  187. $output .= $base64[$c2 & 0x3f];
  188. } while (1);
  189. return '$2a$' . chr(ord('0') + $count / 10) . chr(ord('0') + $count % 10) . '$' . $output;
  190. }
  191. /**
  192. * Generates an Extended DES salt for use in `lithium\security\Password::hash()`.
  193. *
  194. * @param integer $count The base-2 logarithm of the iteration count. Defaults to `18`. Can be
  195. * `1` to `24`. 1 will be stripped from the non-log value, e.g. 2^18 - 1, to
  196. * ensure we don't use a weak DES key.
  197. * @return string The XDES salt.
  198. */
  199. protected static function _genSaltXDES($count = 18) {
  200. $count = (integer) $count;
  201. $count = ($count < 1 || $count > 24) ? 16 : $count;
  202. $count = (1 << $count) - 1;
  203. $base64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  204. $output = '_' . $base64[$count & 0x3f] . $base64[($count >> 6) & 0x3f];
  205. $output .= $base64[($count >> 12) & 0x3f] . $base64[($count >> 18) & 0x3f];
  206. $output .= String::random(3, array('encode' => String::ENCODE_BASE_64));
  207. return $output;
  208. }
  209. /**
  210. * Generates an MD5 salt for use in `lithium\security\Password::hash()`.
  211. *
  212. * @return string The MD5 salt.
  213. */
  214. protected static function _genSaltMD5() {
  215. return '$1$' . String::random(6, array('encode' => String::ENCODE_BASE_64));
  216. }
  217. }
  218. ?>