I'm creating a user account login with PHP and Mysql which will be used by school teachers. To login there are 3 items required : school, username and password. This will allow for 2 of "MRSMITH" in two different schools.
The database is:
schools.login_name is the school part of the login
admin_users.username this is the teacher's username
admin_users.pass_hash(&salt) is the password and its salt
// salt / hash creation - returned SALT & HASH are inserted into user MySQL table
function hash_password($password)
{
$return_array=array();
$salt = mcrypt_create_iv(32, MCRYPT_DEV_RANDOM);
$hexsalt = bin2hex($salt);
$pass_hash = hash('sha256', $password . $hexsalt);
$return_array['SALT'] = $hexsalt;
$return_array['HASH'] = $pass_hash;
return $return_array;
}
(Note, the salt is unique and specific to each user if is is not clear from the above.)
/*
<input type='text' name='school_login_id'>
<input type='text' name='admin_login_id'>
<input type='password' name='admin_password'>
login form above, inouts from $_POST are checked for SQL inject before being passed to the function below to validate login
*/
function validate_login_details($school_login_id, $admin_login_id, $admin_password)
{
global $schools_table, $admin_users_table;
$return_result = false; // invalid unless proven otherwise
// find user and their pass hash and salt
$query = "select S.school_id, pass_hash, pass_salt
from $schools_table as S join $admin_users_table as A using(school_id)
where S.login_name = '$school_login_id' and A.username ='$admin_login_id'";
log_this($query, "loginQueries.log"); // temp debug
$login_res = query_db($query); // wrapper function for MYSQL_QUERY - yes, i will stop using this i promise!
if (mysql_num_rows($login_res) == 1)
{
$db_pass_hash = mysql_result($login_res, 0, "pass_hash");
$db_pass_salt = mysql_result($login_res, 0, "pass_salt");
// re-create hash from supplied password and compare to d/b
$hash_from_entered_pass = hash('sha256', $admin_password . $db_pass_salt);
if ($hash_from_entered_pass == $db_pass_hash)
{
$return_result = true;
}
}
else
{
log_this("mysql_num_rows(\$login_res) not 1 row", "loginfails.log"); // temp debug
}
return $return_result;
}
Before data is passed to validate_login_details() it is checked against these regexps:
School and user login ID:
if (preg_match("/[^-a-z0-9_]/i", $text) )
{
$error_count++;
}
Password:
$match_list = "\x{20}-\x{5f}\x{61}-\x{7e}"; // basic ascii chars excluding backtick
$match_list .= "\x{a1}-\x{ff}"; // extended latin 1 chars excluding control chars
$match_list .= "\x{20ac}\x{201c}\x{201d}"; // euro symbol & left/right double quotation mark (from Word)
$match_list .= "\x{2018}\x{2019}"; // left/right single quotation mark (from word)
if (preg_match("/[^$match_list]/u", $text) )
{
$error_count++;
}
Is this code correct and secure? I have seen many more complex has generation functions, but given that there are no credit cards or personal details involved, is this robust enough?