清理用户密码
在对用户提供的密码进行哈希处理并将其存储在数据库中之前,我应该如何转义或清理这些密码?
当PHP开发人员出于安全目的考虑散列用户的密码时,他们通常倾向于像考虑任何其他用户提供的数据一样考虑这些密码。这个主题经常出现在与密码存储相关的PHP问题中;开发人员通常希望使用诸如(在各种迭代中)等函数来清理密码,然后再对其进行哈希处理并将其存储在数据库中。escape_string()
htmlspecialchars()
addslashes()
在对用户提供的密码进行哈希处理并将其存储在数据库中之前,我应该如何转义或清理这些密码?
当PHP开发人员出于安全目的考虑散列用户的密码时,他们通常倾向于像考虑任何其他用户提供的数据一样考虑这些密码。这个主题经常出现在与密码存储相关的PHP问题中;开发人员通常希望使用诸如(在各种迭代中)等函数来清理密码,然后再对其进行哈希处理并将其存储在数据库中。escape_string()
htmlspecialchars()
addslashes()
由于多种原因,您永远不应该对密码进行转义,修剪或使用任何其他清理机制,您将使用PHP的password_hash()
进行哈希处理,其中最大的原因是因为对密码进行额外的清理需要不必要的额外代码。
你会争辩说(你在每篇接受用户数据用于你的系统的帖子中都会看到它),我们应该清理所有用户输入,你对我们从用户那里接受的所有其他信息都是正确的。密码是不同的。散列密码不能提供任何 SQL 注入威胁,因为字符串在存储到数据库中之前会转换为散列。
对密码进行哈希处理的操作是使密码可以安全地存储在数据库中的行为。哈希函数不会给任何字节赋予特殊意义,因此出于安全原因,不需要清理其输入
如果您遵循允许用户使用他们想要的密码/短语的咒语,并且您不限制密码,则允许任何长度,任意数量的空格和任何特殊字符散列将使密码/密码短语安全,无论密码中包含什么。截至目前,最常见的哈希(默认),PASSWORD_BCRYPT
,将密码转换为60个字符宽的字符串,其中包含随机盐以及散列密码信息和成本(创建哈希的算法成本):
PASSWORD_BCRYPT用于使用CRYPT_BLOWFISH算法创建新的密码哈希。这将始终导致使用“$2y$”加密格式的哈希值,该格式始终为 60 个字符宽。
随着向函数中添加不同的哈希方法,存储哈希的空间要求可能会发生变化,因此最好在存储的哈希的列类型上更大,例如 or 。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 "dessert topping" & a <floor wax>! " // double quotes, ampersand and braces have been changed
string(65) "I'm a "dessert topping" & a <floor wax>! " // 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
在密码验证中使用结果之前,您必须通过您选择的清理方法运行发布的密码。这是一组不必要的步骤,将使哈希不会更好。
使用的 PHP 版本低于 5.5?您可以使用兼容包。password_hash()
您真的不应该使用MD5密码哈希。
在对密码进行哈希处理之前,应按照 RFC 7613 的第 4 节中所述对其进行规范化。特别:
- 附加映射规则:非 ASCII 空间的任何实例都必须映射到 ASCII 空间 (U+0020);非 ASCII 空间是具有 Unicode 常规类别“Zs”的任何 Unicode 码位(U+0020 除外)。
和:
- 规范化规则:Unicode 规范化表单 C (NFC) 必须应用于所有字符。
这将尝试确保如果用户键入相同的密码但使用不同的输入法,则仍应接受该密码。