From: Valerie Pond Date: Fri, 5 Jul 2024 23:07:16 +0000 (+0800) Subject: Security: check passwords against Have I Been Pwned X-Git-Url: https://jfr.im/git/irc/unrealircd/unrealircd-webpanel.git/commitdiff_plain/579020f8c67f7bdfd9d530a25b701af6ee53c412 Security: check passwords against Have I Been Pwned This commit adds functionality to check with the API at https://haveibeenpwned.com to check if your password has been leaked as part of a data breach. The check uses a k-Anonymity model and so does not share your password nor your password hash. Nice and safe. --- diff --git a/Classes/class-paneluser.php b/Classes/class-paneluser.php index 0a3cf8a..1757e30 100644 --- a/Classes/class-paneluser.php +++ b/Classes/class-paneluser.php @@ -71,17 +71,23 @@ class PanelUser { GLOBAL $config; $hash_needs_updating = false; - + $p2 = $password; if (str_starts_with($this->passhash, "peppered:")) { /* Argon2 with pepper */ $password = hash_hmac("sha256", $password, $config['secrets']['pepper']); if (password_verify($password, substr($this->passhash,9))) + { + $this->HIBP(sha1($p2)); return true; - } else { + } + } + else + { /* Old standard argon2 */ if (password_verify($password, $this->passhash)) { + $this->HIBP(sha1($p2)); $hash_needs_updating = true; return true; } @@ -187,6 +193,44 @@ class PanelUser $arr = ['info' => $array, 'user' => $this]; Hook::run(HOOKTYPE_EDIT_USER, $arr); } + + /** Have I Been Pwned + * Check password against HIBP to let them know about their + * leaked password. + * @param string $password_hash This should be a pre-hashed sha1 password + */ + function HIBP($password_hash) + { + $url = "https://api.pwnedpasswords.com/range/".substr($password_hash,0,5); + $end = substr($password_hash,5); + $ch = curl_init($url); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + + if (curl_errno($ch)) + { + error_log("[error] Could not check against Have I Been Pwned API"); + return; + } + $data = explode("\r\n",$response); + curl_close($ch); + $count = count($data); + $i = 1; + foreach($data as $dat) + { + $result = explode(":",$dat); + error_log("Checking $i of $count: ".substr($result[0],0,5)." => ".substr(strtoupper($end), 0,5)); + if ($result[0] == strtoupper($end)) + { + error_log("FOUND"); + $this->add_meta("hibp", $result[1]); + return; + } + $i++; + } + } } diff --git a/index.php b/index.php index 9734271..6ac2254 100644 --- a/index.php +++ b/index.php @@ -28,9 +28,20 @@ $stats = (object) $array_of_stats; $userlist = []; Hook::run(HOOKTYPE_GET_USER_LIST, $userlist); $num_of_panel_admins = count($userlist); - +$current_user = unreal_get_current_user(); +if (isset($current_user->user_meta['hibp'])) +{ + $num = $current_user->user_meta['hibp']; + Message::Fail("
Urgent
","Your password was found in a data breach $num time(s).", + "Please update your password immediately"); +} ?>