Skip to content

Commit a9481e1

Browse files
committed
Implement bcrypt password storage
1 parent 3b54ca9 commit a9481e1

File tree

2 files changed

+35
-29
lines changed

2 files changed

+35
-29
lines changed

etc/config.sample.json

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"reason": "This password is too simple and well known."
4343
}
4444
],
45+
"user_password_bcrypt_cost": 12,
4546
"user_password_pepper": "bnetdocs-INSERTRANDOMVALUEHERE",
4647
"user_register_disabled": false,
4748
"user_register_requirements": {

src/libraries/User.php

+34-29
Original file line numberDiff line numberDiff line change
@@ -159,33 +159,29 @@ public function changeEmail($new_email) {
159159
}
160160

161161
public function changePassword($new_password) {
162-
$password_hash = null; $password_salt = null;
163-
self::createPassword($new_password, $password_hash, $password_salt);
162+
$password_hash = self::createPassword($new_password);
164163
if (!isset(Common::$database)) {
165164
Common::$database = DatabaseDriver::getDatabaseObject();
166165
}
167166
$successful = false;
168167
try {
169-
$stmt = Common::$database->prepare("
168+
$stmt = Common::$database->prepare('
170169
UPDATE `users` SET
171-
`password_hash` = :password_hash,
172-
`password_salt` = :password_salt
170+
`password_hash` = :password_hash, `password_salt` = NULL
173171
WHERE `id` = :user_id;
174-
");
172+
');
175173
$stmt->bindParam(":user_id", $this->id, PDO::PARAM_INT);
176174
$stmt->bindParam(":password_hash", $password_hash, PDO::PARAM_STR);
177-
$stmt->bindParam(":password_salt", $password_salt, PDO::PARAM_STR);
178175
$successful = $stmt->execute();
179176
$stmt->closeCursor();
180177
if ($successful) {
181178
$this->password_hash = (string) $password_hash;
182-
$this->password_salt = (string) $password_salt;
183179
$key = "bnetdocs-user-" . $this->id;
184180
$obj = Common::$cache->get($key);
185181
if ($obj !== false) {
186182
$obj = unserialize($obj);
187183
$obj->password_hash = $this->password_hash;
188-
$obj->password_salt = $this->password_salt;
184+
$obj->password_salt = null;
189185
$obj = serialize($obj);
190186
Common::$cache->set($key, $obj, 300);
191187
}
@@ -229,13 +225,31 @@ public function changeUsername($new_username) {
229225
}
230226

231227
public function checkPassword($password) {
232-
if (is_null($this->password_hash)
233-
|| is_null($this->password_salt))
228+
if (is_null($this->password_hash)) {
229+
// no password set
234230
return false;
235-
$pepper = Common::$config->bnetdocs->user_password_pepper;
236-
$salt = $this->password_salt;
237-
$hash = strtoupper(hash("sha256", $password.$salt.$pepper));
238-
return ($hash === strtoupper($this->password_hash));
231+
}
232+
233+
if (substr($this->password_hash, 0, 1) == '$') {
234+
// new style bcrypt password
235+
236+
$cost = Common::$config->bnetdocs->user_password_bcrypt_cost;
237+
$match = password_verify($password, $this->password_hash);
238+
$rehash = password_needs_rehash(
239+
$this->password_hash, PASSWORD_BCRYPT, array('cost' => $cost)
240+
);
241+
242+
return ($match && !$rehash); // will deny if not match or needs rehash
243+
244+
} else {
245+
// old style sha256 password
246+
247+
$pepper = Common::$config->bnetdocs->user_password_pepper;
248+
$salt = $this->password_salt;
249+
$hash = strtoupper(hash('sha256', $password.$salt.$pepper));
250+
251+
return ($hash === strtoupper($this->password_hash));
252+
}
239253
}
240254

241255
public static function create(
@@ -244,8 +258,7 @@ public static function create(
244258
if (!isset(Common::$database)) {
245259
Common::$database = DatabaseDriver::getDatabaseObject();
246260
}
247-
$password_hash = null; $password_salt = null;
248-
self::createPassword($password, $password_hash, $password_salt);
261+
$password_hash = self::createPassword($password);
249262
$successful = false;
250263
try {
251264
$stmt = Common::$database->prepare("
@@ -255,15 +268,13 @@ public static function create(
255268
`options_bitmask`, `timezone`
256269
) VALUES (
257270
NULL, :email, :username, :display_name, NOW(),
258-
NULL, :password_hash, :password_salt,
259-
:options_bitmask, NULL
271+
NULL, :password_hash, NULL, :options_bitmask, NULL
260272
);
261273
");
262274
$stmt->bindParam(":email", $email, PDO::PARAM_STR);
263275
$stmt->bindParam(":username", $username, PDO::PARAM_STR);
264276
$stmt->bindParam(":display_name", $display_name, PDO::PARAM_STR);
265277
$stmt->bindParam(":password_hash", $password_hash, PDO::PARAM_STR);
266-
$stmt->bindParam(":password_salt", $password_salt, PDO::PARAM_STR);
267278
$stmt->bindParam(":options_bitmask", $options_bitmask, PDO::PARAM_INT);
268279
$successful = $stmt->execute();
269280
$stmt->closeCursor();
@@ -276,15 +287,9 @@ public static function create(
276287
}
277288
}
278289

279-
public static function createPassword($password, &$hash, &$salt) {
280-
$pepper = Common::$config->bnetdocs->user_password_pepper;
281-
282-
$gmp = gmp_init(time());
283-
$gmp = gmp_mul($gmp, mt_rand());
284-
$gmp = gmp_mul($gmp, gmp_random_bits(64));
285-
$salt = strtoupper(gmp_strval($gmp, 36));
286-
287-
$hash = strtoupper(hash("sha256", $password.$salt.$pepper));
290+
public static function createPassword($password) {
291+
$cost = Common::$config->bnetdocs->user_password_bcrypt_cost;
292+
return password_hash($password, PASSWORD_BCRYPT, array('cost' => $cost));
288293
}
289294

290295
public static function findIdByEmail($email) {

0 commit comments

Comments
 (0)