Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
password.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * A Compatibility library with PHP 5.5's simplified password hashing API.
4  *
5  * @author Anthony Ferrara <ircmaxell@php.net>
6  * @license http://www.opensource.org/licenses/mit-license.html MIT License
7  * @copyright 2012 The Authors
8  */
9 
10 if (!defined('PASSWORD_DEFAULT')) {
11 
12  define('PASSWORD_BCRYPT', 1);
13  define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
14 
15  /**
16  * Hash the password using the specified algorithm
17  *
18  * @param string $password The password to hash
19  * @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
20  * @param array $options The options for the algorithm to use
21  *
22  * @return string|false The hashed password, or false on error.
23  */
24  function password_hash($password, $algo, array $options = array()) {
25  if (!function_exists('crypt')) {
26  trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
27  return null;
28  }
29  if (!is_string($password)) {
30  trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
31  return null;
32  }
33  if (!is_int($algo)) {
34  trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
35  return null;
36  }
37  switch ($algo) {
38  case PASSWORD_BCRYPT:
39  // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
40  $cost = 10;
41  if (isset($options['cost'])) {
42  $cost = $options['cost'];
43  if ($cost < 4 || $cost > 31) {
44  trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
45  return null;
46  }
47  }
48  // The length of salt to generate
49  $raw_salt_len = 16;
50  // The length required in the final serialization
51  $required_salt_len = 22;
52  $hash_format = sprintf("$2y$%02d$", $cost);
53  break;
54  default:
55  trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
56  return null;
57  }
58  if (isset($options['salt'])) {
59  switch (gettype($options['salt'])) {
60  case 'NULL':
61  case 'boolean':
62 
63  case 'integer':
64  case 'double':
65  case 'string':
66  $salt = (string) $options['salt'];
67  break;
68  case 'object':
69  if (method_exists($options['salt'], '__tostring')) {
70  $salt = (string) $options['salt'];
71  break;
72  }
73  case 'array':
74  case 'resource':
75  default:
76  trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
77  return null;
78  }
79  if (strlen($salt) < $required_salt_len) {
80  trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
81  return null;
82  } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
83  $salt = str_replace('+', '.', base64_encode($salt));
84  }
85  } else {
86  $buffer = '';
87  $buffer_valid = false;
88  if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
89  $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
90  if ($buffer) {
91  $buffer_valid = true;
92  }
93  }
94  if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
95  $buffer = openssl_random_pseudo_bytes($raw_salt_len);
96  if ($buffer) {
97  $buffer_valid = true;
98  }
99  }
100  if (!$buffer_valid && is_readable('/dev/urandom')) {
101  $f = fopen('/dev/urandom', 'r');
102  $read = strlen($buffer);
103  while ($read < $raw_salt_len) {
104  $buffer .= fread($f, $raw_salt_len - $read);
105  $read = strlen($buffer);
106  }
107  fclose($f);
108  if ($read >= $raw_salt_len) {
109  $buffer_valid = true;
110  }
111  }
112  if (!$buffer_valid || strlen($buffer) < $raw_salt_len) {
113  $bl = strlen($buffer);
114  for ($i = 0; $i < $raw_salt_len; $i++) {
115  if ($i < $bl) {
116 
117  $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
118  } else {
119  $buffer .= chr(mt_rand(0, 255));
120  }
121  }
122  }
123  $salt = str_replace('+', '.', base64_encode($buffer));
124  }
125  $salt = substr($salt, 0, $required_salt_len);
126 
127  $hash = $hash_format . $salt;
128 
129  $ret = crypt($password, $hash);
130 
131  if (!is_string($ret) || strlen($ret) <= 13) {
132  return false;
133  }
134 
135  return $ret;
136  }
137 
138  /**
139  * Get information about the password hash. Returns an array of the information
140  * that was used to generate the password hash.
141  *
142  * array(
143  * 'algo' => 1,
144  * 'algoName' => 'bcrypt',
145  * 'options' => array(
146  * 'cost' => 10,
147  * ),
148  * )
149  *
150  * @param string $hash The password hash to extract info from
151  *
152  * @return array The array of information about the hash.
153  */
154  function password_get_info($hash) {
155  $return = array(
156  'algo' => 0,
157  'algoName' => 'unknown',
158  'options' => array(),
159  );
160  if (substr($hash, 0, 4) == "$2y$%d$" && strlen($hash) == 60) {
161  $return['algo'] = PASSWORD_BCRYPT;
162  $return['algoName'] = 'bcrypt';
163  list($cost) = sscanf($hash, "$2y$%d$");
164  $return['options']['cost'] = $cost;
165  }
166  return $return;
167  }
168 
169  /**
170  * Determine if the password hash needs to be rehashed according to the options provided
171  *
172  * If the answer is true, after validating the password using password_verify, rehash it.
173  *
174  * @param string $hash The hash to test
175  * @param int $algo The algorithm used for new password hashes
176  * @param array $options The options array passed to password_hash
177  *
178  * @return boolean True if the password needs to be rehashed.
179  */
180  function password_needs_rehash($hash, $algo, array $options = array()) {
181  $info = password_get_info($hash);
182  if ($info['algo'] != $algo) {
183  return true;
184  }
185  switch ($algo) {
186  case PASSWORD_BCRYPT:
187  $cost = isset($options['cost']) ? $options['cost'] : 10;
188  if ($cost != $info['options']['cost']) {
189  return true;
190  }
191  break;
192  }
193  return false;
194  }
195 
196  /**
197  * Verify a password against a hash using a timing attack resistant approach
198  *
199  * @param string $password The password to verify
200  * @param string $hash The hash to verify against
201  *
202  * @return boolean If the password matches the hash
203  */
204  function password_verify($password, $hash) {
205  if (!function_exists('crypt')) {
206  trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
207  return false;
208  }
209  $ret = crypt($password, $hash);
210  if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
211  return false;
212  }
213 
214  $status = 0;
215  for ($i = 0; $i < strlen($ret); $i++) {
216  $status |= (ord($ret[$i]) ^ ord($hash[$i]));
217  }
218 
219  return $status === 0;
220  }
221 }
222