我有一个系统的想法,以登录用户,并验证他们的登录页面。
我意识到有很多系统,但我主要是好奇我的想法是否好。我做了一些挖掘,但大多数结果似乎遗漏了我一直认为重要的实践(如密码加密等)。我可能会更努力地寻找预制的解决方案,因为它可能更安全,但我没有真正使用过应用程序安全,希望能得到一些反馈。
当用户登录时,他们的姓名和密码将根据数据库进行验证,密码使用SHA256和随机生成的salt进行加密,整个字符串( salt和加密的密码都是128个字符)。long)。下面是密码验证码:
function ValidatePassword($password, $correctHash)
{
$salt = substr($correctHash, 0, 64); //get the salt from the front of the hash
$validHash = substr($correctHash, 64, 64); //the SHA256
$testHash = hash("sha256", $salt . $password); //hash the password being tested
//if the hashes are exactly the same, the password is valid
return $testHash === $validHash;
}如果登录有效,则会为其分配一个令牌。此令牌类似于密码加密,但存储加密的纪元以及另一个随机盐。令牌、登录时间、期满时间和用户名被存储在DB中,并且用户名和令牌作为会话信息被发送。
下面是创建令牌的代码:
function loginUser($email)
{
$thetime = time();
$ip = $_SERVER['REMOTE_ADDR'];
$dbuser="///";
$dbpass="///";
$dbtable="tokens";
mysql_connect(localhost,$dbuser,$dbpass);
mysql_select_db("///") or die( "Unable to select database");
//Generate a salt
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
//Hash the salt and the current time to get a random token
$hash = hash("sha256", $salt . $password);
//Prepend the salt to the hash
$final = $salt . $hash;
$exptime = $thetime + 3600;
//Store this value into the db
$query = "INSERT INTO `spanel`.`tokens` VALUES ('$final', $thetime, $exptime, $thetime, '$ip', MD5('$email') )";
mysql_query($query) or die ("Could not create token.");
//Store the data into session vars
$_SESSION['spanel_email'] = $email;
$_SESSION['spanel_token'] = $final;
return true;
}当他们到达一个页面时,他们拥有的令牌和用户名将根据数据库进行检查。如果检查正确,则更新过期时间并加载页面。下面是代码:
function validateUser($page)
{
//Grab some vars
$thetime = time();
$ip = $_SERVER['REMOTE_ADDR'];
$token = $_SESSION['spanel_token'];
$email = $_SESSION['spanel_email'];
$dbuser="///";
$dbpass="///";
$dbtable="tokens";
mysql_connect(localhost,$dbuser,$dbpass);
mysql_select_db("///") or die( "Unable to select database");
//Global var
//Get the var for token expire
$token_expire = 3600;
//Validate the token
$query = "SELECT * FROM `tokens` WHERE `token` LIKE '$token' AND `user_id` LIKE MD5('$email') AND `exp` > $thetime";
$result = mysql_query($query) or die(mysql_error());
//Check if we have a valid result
if ( mysql_num_rows($result) != 1 ) {
//Logout the user
//Destroy the session
session_destroy();
//Redirect
header("location: /spanel/login.php?denied=1");
exit();
//(Since the token is already invalid, there's no reason to reset it as invalid)
}
$row = mysql_fetch_assoc($result);
//Update the token with our lastseen
$newexp = $thetime + $token_expire;
$query = "UPDATE `spanel`.`tokens` SET `exp` = $newexp, `lastseen_ip` = $thetime, `lastseen_ip` = '$ip' WHERE `token` LIKE '$token'";
mysql_query($query);
}反馈(好的和坏的)是值得欣赏的。就像我说的,我没有做太多的安全工作,希望能得到一些指点。
编辑:我担心我高估了自己创建登录系统的能力。这么说吧,如果你决定停止试图弄清楚混乱的混乱,我可以理解,这可能是有缺陷的想法。
不过,以下是来自登录页面的php代码。(在这里说了这些之后,我意识到仅仅发布密码是一个很大的禁忌)。
$email = $_POST['email'];
$password = $_POST['password'];
$dbuser="///";
$dbpass="///";
$dbtable="///";
mysql_connect(localhost,$dbuser,$dbpass);
mysql_select_db("spanel") or die( "Unable to select database");
$query = "SELECT * FROM users WHERE `email` LIKE '$email'";
$result=mysql_query($query) or die(mysql_error());
$num=mysql_num_rows($result);
$row = mysql_fetch_array($result);
if ( ValidatePassword($password, $row['hash']) == true ) {
loginUser($email);
header("location: /spanel/index.php");
} else {
echo "<p>Login Failed.</p>";
}下面是在创建帐户时生成密码、盐和散列的代码。
function HashPassword($password)
{
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); //get 256 random bits in hex
$hash = hash("sha256", $salt . $password); //prepend the salt, then hash
//store the salt and hash in the same string, so only 1 DB column is needed
$final = $salt . $hash;
return $final;
}感谢你的反馈,我很高兴我缺乏知识的问题是在这里发现的,而不是在攻击之后。
发布于 2012-05-15 01:08:26
首先,这样的散列是不安全的。sha256很容易被破解,至少对于短密码是这样。您必须使用一些hash stretching。看看是否可以找到一些PBKDF2实现,或者使用维基百科对sha256的"for循环“的建议。此外,我不明白使用更多会话变量可以实现什么效果。我不明白validateuser()是做什么的,它仍然依赖于会话id,或者我遗漏了什么。
发布于 2012-05-16 02:53:57
与sivann类似,我也看不出增加spanel_token的原因。它所做的一切似乎都是为了确保会话在令牌过期后不再有效。由于WHERE条件的token和user_id的值都存储在会话中,并且仅在登录期间设置,因此它们不会更改。But session expiration can be implemented much easier.
但除此之外,更重要的是:您的代码容易受到SQL注入的攻击。有了你在这里发布的知识,一切都会变得简单。您所需要做的就是执行以下步骤:
UNION SELECT注入的用户列数:‘UNION SELECT null,…,null,其中''=‘
如果输入了错误的列数,脚本将抛出MySQL错误,否则会显示“登录失败”。将会出现。感谢您。使用以下查询执行
值000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55被注入到每条记录中,而不是原始的哈希列值。前面的0是盐,其余的字符串是空密码字符串的加盐的SHA-256哈希值,这将产生有效的密码。
‘UNION SELECT t2。* FROM users t1 RIGHT JOIN (SELECT email,'000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55’hash FROM users LIMIT 1) t2 (email) WHERE ''='
这应该足以作为任何用户进行“身份验证”。
发布于 2012-05-15 01:15:12
仅评论一下salt/hash系统:将salt与密码一起存储在数据库中有点违背了salt的目的--如果您的数据库被攻破,那么从安全的角度来看,salt就变得无用了,因为它正好可以帮助猜测/破坏安全性。盐的目的是通过将适当长度的秘密字符串添加到要散列的值中,来增加猜测散列单词的时间。
https://stackoverflow.com/questions/10587890
复制相似问题