清除用户密码

Cleansing User Passwords

提问人:Jay Blanchard 提问时间:4/15/2016 最后编辑:TarynJay Blanchard 更新时间:3/23/2017 访问量:7432

问:

在对用户提供的密码进行哈希处理并将其存储在数据库中之前,我应该如何转义或清除这些密码?

当PHP开发人员出于安全目的考虑对用户的密码进行哈希处理时,他们通常倾向于像对待任何其他用户提供的数据一样考虑这些密码。这个主题经常出现在与密码存储相关的PHP问题中;开发人员通常希望在对密码进行哈希处理并将其存储在数据库中之前,使用(在各种迭代中)等函数来清理密码。escape_string()htmlspecialchars()addslashes()

php sql pdo 哈希

评论

1赞 MSS 11/7/2017
您可以使用 base64 编码
0赞 Jay Blanchard 11/7/2017
不@MSS,你不应该,因为 base64 是编码,而不是加密散列。密码应始终进行哈希处理
1赞 MSS 11/8/2017
我的意思是在哈希;)之前
0赞 Jay Blanchard 11/8/2017
在散列之前,您不应该也不需要这样做。这将导致您不得不编写不必要的额外代码@MSS

答:

102赞 Jay Blanchard 4/15/2016 #1

由于多种原因,您永远不应该对将使用 PHP 的 password_hash() 进行哈希处理的密码进行转义、修剪或使用任何其他清理机制,其中最大的一个原因是因为对密码进行额外的清理需要不必要的额外代码。

你会争辩说(你在每篇接受用户数据用于你的系统的帖子中都能看到这一点)我们应该清理所有用户输入,而你对于我们从用户那里接受的所有其他信息都是正确的。密码是不同的。哈希密码不能提供任何 SQL 注入威胁,因为字符串在存储在数据库中之前会转换为哈希。

对密码进行哈希处理的行为是使密码安全地存储在数据库中的行为。哈希函数不会为任何字节赋予特殊含义,因此出于安全原因,不需要清理其输入

如果您遵循允许用户使用他们想要的密码/短语的口头禅,并且您不限制密码,则允许任何长度、任意数量的空格和任何特殊字符散列将使密码/密码安全,无论密码中包含什么。截至目前,最常见的哈希(默认值)PASSWORD_BCRYPT,将密码转换为一个 60 个字符宽的字符串,其中包含随机盐以及哈希密码信息和成本(创建哈希的算法成本):

PASSWORD_BCRYPT 用于使用 CRYPT_BLOWFISH 算法创建新的密码哈希。这将始终导致使用“$2y$”加密格式的哈希值,该格式始终为 60 个字符宽。

存储哈希的空间要求可能会随着函数中添加不同的哈希方法而更改,因此最好在存储哈希的列类型上加大,例如 或 。VARCHAR(255)TEXT

您可以使用完整的 SQL 查询作为密码,它会被哈希处理,使其无法被 SQL 引擎执行,例如,

SELECT * FROM `users`;

可以散列到$2y$10$1tOKcWUWBW5gBka04tGMO.BH7gs/qjAHZsC5wyG0zmI2C.KgaqU5G

让我们看看不同的清理方法如何影响密码 -

密码是(密码末尾有 5 个空格,此处不显示。I'm a "dessert topping" & a <floor wax>!

当我们应用以下修剪方法时,我们会得到一些截然不同的结果:

var_dump(trim($_POST['upassword']));
var_dump(htmlentities($_POST['upassword']));
var_dump(htmlspecialchars($_POST['upassword']));
var_dump(addslashes($_POST['upassword']));
var_dump(strip_tags($_POST['upassword']));

结果:

string(40) "I'm a "dessert topping" & a <floor wax>!" // spaces at the end are missing
string(65) "I'm a &quot;dessert topping&quot; &amp; a &lt;floor wax&gt;!     " // double quotes, ampersand and braces have been changed
string(65) "I'm a &quot;dessert topping&quot; &amp; a &lt;floor wax&gt;!     " // same here
string(48) "I\'m a \"dessert topping\" & a <floor wax>!     " // escape characters have been added
string(34) "I'm a "dessert topping" & a !     " // looks like we have something missing

当我们将这些发送到时会发生什么?它们都会被哈希处理,就像上面的查询一样。当您尝试验证密码时,问题就出现了。如果我们使用其中一种或多种方法,我们必须在将它们与 password_verify() 进行比较之前重新使用它们。以下操作将失败:password_hash()

password_verify($_POST['upassword'], $hashed_password); // where $hashed_password comes from a database query

在密码验证中使用结果之前,您必须通过您选择的清理方法运行发布的密码。这是一组不必要的步骤,不会使哈希值更好。


使用低于 5.5 的 PHP 版本?您可以使用兼容包password_hash()

您真的不应该使用 MD5 密码哈希

评论

13赞 Jay Blanchard 4/15/2016
不。如果他使用尾随空格创建密码(这是允许的),则必须在登录@DanBracuk时使用它们
12赞 Jay Blanchard 4/15/2016
怎么会这样@DanBracuk?如果我们允许用户设置他/她想要的密码,包括前导/尾随空格?
16赞 I wrestled a bear once. 4/15/2016
这就是为什么大多数事情都要求您输入两次所选密码的原因。如果用户意外添加了空格,他们会在进一步操作之前弄清楚。如果用户是故意这样做的,那就不是问题了。
4赞 DavidS 4/15/2016
@MargaretBloom,经验法则只是一种启发式方法。我们有时仍然需要考虑清楚,例如密码。你说“没有人知道未来事情会如何变化”,但似乎如果有什么变化,那就是我们在将数据放入数据库之前逃避数据的方式,在这种情况下,当用户的密码不再与我们存储的密码匹配时,他们会发现自己被锁定了。不转义密码哈希与转义密码哈希的危险是什么?
3赞 Steve Jessop 4/15/2016
确切地说:您当然会在将哈希正确传递给参数化 SQL 查询的有限意义上“转义哈希”,其中 SQL 连接器中的某些代码可能会也可能不会对它执行任何对应于“转义”的事情,您不知道也不在乎。你只需要编写任何特定的代码来实现这一目标,因为对于你所有的SQL查询来说,这都是完全例行公事的,除非你以前做过一些糟糕的人生决定。
37赞 legoscia 4/15/2016 #2

在对密码进行哈希处理之前,应按照 RFC 7613 第 4 节中的说明对其进行规范化。特别:

  1. 附加映射规则:任何非 ASCII 空间的实例都必须 映射到 ASCII 空间 (U+0020);非 ASCII 空格是任何 Unicode 具有 Unicode 通用类别“Zs”的码位(带有 U+0020 除外)。

和:

  1. 规范化规则:Unicode 规范化形式 C (NFC) 必须是 应用于所有字符。

这试图确保如果用户键入相同的密码但使用不同的输入法,则仍应接受该密码。

评论

3赞 Margaret Bloom 4/15/2016
@DavidS,一台超级闪亮的北美Mac Book(Joe在离开前用过)和一台国际化程度很差的台湾网吧电脑(Joe试图用来下载的是航班返程登机牌)。
2赞 DavidS 4/15/2016
听起来很沙文主义。:-)谢谢。
3赞 ruakh 4/15/2016
嗯。如果执行此操作,则还应验证密码以拒绝任何包含尚未分配的字符的密码。如果用户使用 NEWFANGLED SPACE,您的应用程序无法识别它,因此按原样进行哈希处理,然后您升级 Unicode 字符数据库,突然 NEWFANGLED SPACE 在哈希之前映射到 SPACE,那么他将无法再输入您的应用程序将哈希到旧哈希的密码。
1赞 Jay Blanchard 4/28/2016
好奇 - 如果应用程序自始至终都是 UTF-8,如何将非 Ascii 空格输入到密码中?
4赞 Kuba hasn't forgotten Monica 4/28/2016
@JayBlanchard因为当您在一台机器上按下空格键时,当您在另一台机器上按下它时,您可能会得到两个不同的 Unicode 码位,并且它们将具有两种不同的 UTF-8 编码,而用户却不知道任何事情。可以说,这是一个你希望忽略的问题,但RFC 7613就是从这些现实生活中的问题中诞生的,它不是一个临时的建议。