Hi archers, Here's a patch that adds support for storing salted passwords in the database. The salt is a random string for each user and is stored along with the password in the Users table. Salt is created and password is salted when old users log in. New users get salted passwords when they register. What do you think?
Denis.
From e80ad5b544d14138542675f85d52ff8e1fb28d99 Mon Sep 17 00:00:00 2001 From: Denis <[email protected]> Date: Mon, 5 Apr 2010 09:41:30 -0400 Subject: [PATCH] Support for storing salted passwords --- support/schema/aur-schema.sql | 1 + web/html/passreset.php | 5 +++- web/lib/acctfuncs.inc | 58 ++++++++++++++++++++++++++-------------- web/lib/aur.inc | 31 ++++++++++++++++++++++ 4 files changed, 74 insertions(+), 21 deletions(-) diff --git a/support/schema/aur-schema.sql b/support/schema/aur-schema.sql index 67e20a7..250d405 100644 --- a/support/schema/aur-schema.sql +++ b/support/schema/aur-schema.sql @@ -26,6 +26,7 @@ CREATE TABLE Users ( Username CHAR(32) NOT NULL, Email CHAR(64) NOT NULL, Passwd CHAR(32) NOT NULL, + Salt CHAR(32) NOT NULL DEFAULT '', ResetKey CHAR(32) NOT NULL DEFAULT '', RealName CHAR(64) NOT NULL DEFAULT '', LangPreference CHAR(5) NOT NULL DEFAULT 'en', diff --git a/web/html/passreset.php b/web/html/passreset.php index 6fbd1ca..0f98593 100644 --- a/web/html/passreset.php +++ b/web/html/passreset.php @@ -31,10 +31,13 @@ if (isset($_GET['resetkey'], $_POST['email'], $_POST['password'], $_POST['confir if (empty($error)) { $dbh = db_connect(); + $salt = generate_salt(); + $hash = salted_hash($password, $salt); # The query below won't affect any records unless the ResetKey # and Email combination is correct and ResetKey is nonempty $q = "UPDATE Users - SET Passwd = '".md5($password)."', + SET Passwd = '$hash', + Salt = '$salt', ResetKey = '' WHERE ResetKey != '' AND ResetKey = '".mysql_real_escape_string($resetkey)."' diff --git a/web/lib/acctfuncs.inc b/web/lib/acctfuncs.inc index 91b6249..9c172bb 100644 --- a/web/lib/acctfuncs.inc +++ b/web/lib/acctfuncs.inc @@ -265,17 +265,14 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", } else { if ($TYPE == "new") { # no errors, go ahead and create the unprivileged user - - # md5hash the password - $P = md5($P); - $q = "INSERT INTO Users (AccountTypeID, Suspended, Username, Email, "; - $q.= "Passwd, RealName, LangPreference, IRCNick, NewPkgNotify) "; - $q.= "VALUES (1, 0, '".mysql_real_escape_string($U)."'"; - $q.= ", '".mysql_real_escape_string($E)."'"; - $q.= ", '".mysql_real_escape_string($P)."'"; - $q.= ", '".mysql_real_escape_string($R)."'"; - $q.= ", '".mysql_real_escape_string($L)."'"; - $q.= ", '".mysql_real_escape_string($I)."'"; + $salt = generate_salt(); + $P = salted_hash($P, $salt); + $escaped = array_map(mysql_real_escape_string, + array($U, $E, $P, $salt, $R, $L, $I)); + $q = "INSERT INTO Users (" . + "AccountTypeID, Suspended, Username, Email, Passwd, Salt" . + ", RealName, LangPreference, IRCNick, NewPkgNotify) " . + "VALUES (1, 0, '" . implode("', '", $escaped) . "'"; if ($N) { $q.= ", 1)"; } else { @@ -298,7 +295,6 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", } else { # no errors, go ahead and modify the user account - # md5 hash the password $q = "UPDATE Users SET "; $q.= "Username = '".mysql_real_escape_string($U)."'"; if ($T) { @@ -311,7 +307,9 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", } $q.= ", Email = '".mysql_real_escape_string($E)."'"; if ($P) { - $q.= ", Passwd = '".mysql_real_escape_string(md5($P))."'"; + $salt = generate_salt(); + $hash = salted_hash($P, $salt); + $q .= ", Passwd = '$hash', Salt = '$salt'"; } $q.= ", RealName = '".mysql_real_escape_string($R)."'"; $q.= ", LangPreference = '".mysql_real_escape_string($L)."'"; @@ -738,14 +736,34 @@ function valid_passwd( $userID, $passwd ) { if ( strlen($passwd) > 0 ) { $dbh = db_connect(); - $q = "SELECT ID FROM Users". - " WHERE ID = '$userID'" . - " AND Passwd = '" . md5($passwd) . "'"; - $result = mysql_fetch_row(db_query($q, $dbh)); - if ($result[0]) { - # Is it the right password? - return true; + # get salt for this user + $salt = get_salt($userID); + if ($salt) { + # use salt + $passwd_q = "SELECT ID FROM Users" . + " WHERE ID = '$userID' AND Passwd = '" . + salted_hash($passwd, $salt) . "'"; + $passwd_result = mysql_fetch_row(db_query($passwd_q, $dbh)); + if ($passwd_result[0]) { + return true; + } + } else { + # check without salt + $nosalt_q = "SELECT ID FROM Users". + " WHERE ID = '$userID'" . + " AND Passwd = '" . md5($passwd) . "'"; + $nosalt_result = mysql_fetch_row(db_query($nosalt_q, $dbh)); + if ($nosalt_result[0]) { + # password correct, but salt it first + if (!save_salt($userID, $passwd)) { + trigger_error("Unable to salt user's password;" . + " ID $userID", E_USER_WARNING); + return false; + } + + return true; + } } } return false; diff --git a/web/lib/aur.inc b/web/lib/aur.inc index e0521ab..ec872b1 100644 --- a/web/lib/aur.inc +++ b/web/lib/aur.inc @@ -455,3 +455,34 @@ function mkurl($append) { return substr($out, 5); } + +function get_salt($user_id) +{ + $dbh = db_connect(); + $salt_q = "SELECT Salt FROM Users WHERE ID = '$user_id'"; + $salt_result = mysql_fetch_row(db_query($salt_q, $dbh)); + return $salt_result[0]; +} + +function save_salt($user_id, $passwd) +{ + $dbh = db_connect(); + $salt = generate_salt(); + $hash = salted_hash($passwd, $salt); + $salting_q = "UPDATE Users SET Salt = '$salt'" . + ", Passwd = '$hash' WHERE ID = '$user_id'"; + return db_query($salting_q, $dbh); +} + +function generate_salt() +{ + return md5(uniqid(rand(), true)); +} + +function salted_hash($passwd, $salt) +{ + if (strlen($salt) != 32) { + trigger_error('Salt does not look like an md5 hash', E_USER_WARNING); + } + return md5($salt . $passwd); +} -- 1.7.0.3
