密码验证;这种方式安全吗?

7
我有一个分类广告网站,任何人都可以在上面发布他们的产品广告。
对于每个分类广告,用户都需要输入一个密码(这样他们可以随时删除该广告)。
因此,当有人想要删除一个分类广告时,他们点击分类广告,点击删除按钮,然后输入密码。
我使用MySql作为数据库。
我基本上使用这段代码:
if ($pass==$row['poster_password'])

其中row [poster_password]是从MySql获取的...

你觉得呢? 谢谢


18
请确认您没有在MySQL中保存他们的实际密码... - animuson
1
是的,我会,但是嘿,网站还没有上线,正在开发中... 我现在刚开始关注安全方面... 那我应该如何保存它呢? - user188962
3
阅读https://dev59.com/yHRC5IYBdhLWcg3wJNYN在PHP密码中实现安全哈希和盐。 - Kendall Hopkins
9
@Col. Shrapnel 是的,无论是什么类型的网站,以明文形式存储密码都是绝对不可接受的。没有例外。 - Michael Stum
3
许多用户会重复使用密码。如果我能弄清楚哪个用户使用了哪个密码,我就可以尝试在其他地方使用该密码并查看开启了什么。此外,如果我能删除他人的密码,并且您无法判断是我这样做的(您可能只会认为是用户这样做了),那么我就可以激怒你的整个客户群体:你们会得到报酬,但他们的广告将消失。他们会提起诉讼吗?谁知道呢。 - Mike DeSimone
显示剩余6条评论
8个回答

9

请查看:PHP密码的安全哈希和盐

在将密码存入数据库之前,使用哈希(可能加上一些盐)对其进行处理。将哈希后的密码存储在数据库中(而不是实际密码)。然后从数据库中获取他们的哈希密码,对其输入密码进行哈希并比较哈希密码。

以下是一些伪代码:

password_hash = hash(password_cleartext)
# store password_hash in database

稍后:

input_password_hash = hash(input_password_cleartext)
fetched_password_hash_from_db = fetch(db, password_hash)
if (input_password_hash == fetched_password_hash_from_db) {
    ... authenticated ...
}

如果你想学习php技术,可以尝试使用以下链接:http://php.net/manual/en/function.sha1.php


6
首先提到哈希算法的点赞加一。请注意,哈希算法与加密完全不同,它们是不同的概念。(同时也不同于校验和,后者未必具有加密安全性)。此外,MD5已经被完全攻破,请勿使用。 - BlueRaja - Danny Pflughoeft
好的,那么哈希是 PHP 的一个函数吧?你有实际的例子吗? - user188962
1
这里的踩有什么话要说吗?还是我只是被踩而没有解释? - dlamotte
@Camran - 请查看 Kendall Hopkins 在原问题的评论中提供的链接。 - MiffTheFox
真的不喜欢有些人在我的答案下回复提问者并且踩我的答案。如果你有一个指向其他问题的链接,那就回答问题,不要只是评论,更不要踩那些不知道其他问题的人。 - dlamotte
@BlueRaja,你有一个很好的机会展示MD5的弱点。不需要多说,只需要一个单词就可以了。如果你愿意:http://stackoverflow.com/questions/2768248/is-md5-really-that-bad - Your Common Sense

4

你的代码看起来很安全,但是你的设计可能需要一些改进。

SQL注入

代码中的危险部分在于将任何从用户收集而来的数据存储到数据库中或向用户显示。因此,在你的示例之前,你必须小心处理。确保你对从用户收集的任何数据进行验证、过滤和转义,包括密码和广告信息。

加密

将密码存储在数据库中的好处是,如果用户忘记密码,你可以让用户通过电子邮件或其他方式检索密码。

然而,如果你确实存储了密码,那么你应该使用一个秘密密钥对它们进行加密,以便如果有人能够直接读取你的数据库,他们不能以明文形式读取所有密码。不过,你仍然需要在某个地方存储秘密密钥,如果有人获取了你的秘密密钥并且有权访问你的数据库,他们就可以访问所有密码。

哈希值(推荐)

最佳做法和更安全的方法是只在数据库中存储单向哈希值(SHA1或SHA256),而不是实际的密码。这样,你就无法检索密码。哈希值意味着通过丢弃一些数据来实现单向。

在这种情况下,你不是检索原始密码,而是对用户输入的密码进行哈希,并将哈希值与存储的哈希值进行比较,以查看是否匹配。如果用户在这种情况下忘记了密码,你不会将密码发送给用户,而是发送一个新的、随机生成的密码。

仅存储哈希值可以进一步保护你的数据,因为即使用户具有读取数据库的访问权限,哈希值也没有任何优势,也没有秘密密钥可以解锁所有哈希值。

在哈希密码时,请确保使用随机盐值并存储盐值,以保护你的哈希列表免受彩虹攻击。

总结

有时你不能选择密码。有时密码来自另一个系统,所以你并不总是有选择的余地,有时你的上级(甚至用户)会要求能够检索密码,但是在可能的情况下,你应该选择更安全的选项。

请注意,所有这些加密和哈希值业务只能部分保护你的服务器免受能够获得只读访问权限的人的攻击。有时候,获取你的数据就足够了,所以如果用户可以读取密码哈希值,他们能否读取你的信用卡号码?

你需要保护你的数据库。你的数据库系统使用安全密码了吗?你只允许本地访问你的数据吗?你是否创建了一个具有最少权限的数据库用户来在应用程序中使用?你是否正确地保护自己免受SQL注入和脚本攻击?

如果有人具有读写数据库的访问权限,整个密码业务就变得无意义。


2

不要将实际密码存储在数据库中,而是存储校验和(MD5,SHA1等)。当您想进行比较时,请对用户提交的值执行校验和,并比较校验和。

这样就不会在内存中保存实际密码。


无论如何,也不会被存储在任何地方。 - Steven
md4、md5、sha0和sha1都是已经被攻破的哈希函数。任何提出漏洞的人将会得到-1分,抱歉这就是规则。应该使用sha256。 - rook
1
除非你在JavaScript中的客户端进行哈希处理,否则在检查时必须将其存储在内存中。但这并不重要——如果攻击者可以访问内存,那么密码存在其中将是你最不用担心的问题之一。 - Tgr
@The Rook,如果sha0被破解了,那么sha256也一样。唯一的区别就是密钥长度。 - Malfist

1

最佳实践是在数据库中保存一个带盐的 sha1 哈希:

if (sha1($pass.$row['poster_salt'])==$row['poster_password'])

(poster_salt是在用户选择密码时生成和保存的随机字符串。)

这样,如果攻击者获得了访问您的数据库的权限,他们仍然无法获取用户的密码(这些密码可能也被用于其他地方 - 大多数人不会为不同的网站选择不同的密码)。

此外,您应该使用安全(HTTPS)连接。并要求足够强大的密码。

(至少如果您想要良好的安全性,在简单的广告列表的情况下可能有点过头。)


md4、md5、sha0和sha1都是已经破解的哈希函数。任何试图利用其漏洞的人都会被评为-1分,很抱歉这就是规则。推荐使用sha256。 - rook
迄今为止最佳答案。@The Rook:这似乎有点不公平,因为SHA1并没有像其他三个算法那样被完全破解(甚至还没有发现碰撞)。 - BlueRaja - Danny Pflughoeft
1
@BlueRaja,您能否演示一下使用碰撞的MD5弱点吗? - Your Common Sense
1
在sha1中找到碰撞大约需要2^60次操作(而不是暴力攻击所需的2^80次)。使用4或6个核心的好电脑,三年就可以完成。你可以称其为在某种技术意义上被破解了,但说它对于分类广告网站来说还不够好就很愚蠢了。特别是因为在原始的PHP中没有更好的选择(你需要hash来进行sha256,而且很少有主机允许安装PECL扩展)。 - Tgr
此外,并不明显纯sha1哈希攻击也适用于加盐的哈希。 - Tgr
显示剩余4条评论

0

这意味着密码以未加密的形式存储在您的密码中。如果是这种情况,输入密码时应使用某种加密方式。一种方法是使用哈希密码的MD5函数进行加密。

在执行插入操作时,您可以执行以下操作

Insert into table(email, password, whatever) values('$email', md5($password), whatever)

当进行比较时,您需要执行以下操作

if (md5($pass) == $row['password'])

-1 md4,md5,sha0和sha1是已经被攻破的哈希函数。任何提出漏洞的人都会得到一个-1,抱歉这就是规则。应该使用sha256。 - rook
可以,请您提供一个来源吗? - Gazler
请点击此处查看:*“我们提出的算法[...] 使我们能够在仅几分钟内找到完整的MD5碰撞,并且是在3Ghz Pentium 4上实现的。”* 王等人也提供了一种方法(大约五年前),展示如何使用纸和笔找到MD4碰撞。SHA1最近被破解,但情况没有那么糟糕(仍然没有已知的碰撞)。 - BlueRaja - Danny Pflughoeft

0

我的建议如下:

用户表应该有两列,一列叫做“密码”,另一列叫做“盐”。

$password =  'youruserpassword in plain text';
$salt = bin2hex(openssl_random_pseudo_bytes(32));
$passtostore = hash_hmac('sha384', $password, $salt);
insert into users(password, salt) values($passtostore, $salt);

然后验证用户是否输入了正确的密码...

从数据库中检索密码和盐,并进行比对。

if(hash_hmac('sha384',$userpass, $row['salt']) === $row['password']) { 
 // is valid 
 }

0

对了,不要在任何地方存储实际密码。 - quoo
1
我认为这里加密是一个误导性的词。它应该是“密码哈希”,而不是加密。加密听起来像是简单地加密密码,而哈希则无法被“反向”。 - dlamotte
密码应始终进行哈希处理。加密永远不应用于密码。 - rook
啊,谢谢,已经纠正了。由于某种原因,我认为哈希是一种更具体的加密类型。但是...我是前端开发人员,不太经常处理这些东西。 - quoo

0

在存储密码之前,我会对其进行加密,然后在检索时进行解密,以便您可以将其与用户以明文输入的内容(根据您上面的示例代码)进行比较。

此外,请保护自己免受任何SQL注入攻击,否则某人可能会看到您数据库中的所有密码(和其他数据)。


-1加密绝不能用于密码。这是明显违反CWE-257(http://cwe.mitre.org/data/definitions/257.html)的规定。应该使用诸如sha256之类的消息摘要函数。任何提出漏洞的人都会得到-1,这就是规则。 - rook
Rook因声称加密永远不应该使用并引用CWE-257而受到批评,CWE-257表明“使用强大的、不可逆的加密保护存储的密码。” - Stephen P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接