清理用户密码

8 浏览
0 Comments

清理用户密码

在将用户提供的密码哈希化并存储到数据库之前,我应该如何转义或清除它们?

当PHP开发者考虑对用户密码进行哈希以提高安全性时,他们往往会把这些密码看作是其他用户提供的数据。这个问题经常在与密码存储相关的PHP问题中出现;开发者通常希望在哈希密码并存储到数据库之前,使用诸如escape_string()(以各种形式出现)、htmlspecialchars()、addslashes()等函数清除密码。

0
0 Comments

(Cleansing User Passwords)问题的出现原因是为了确保用户输入相同的密码时,不同的输入方法也能被接受。解决方法是根据RFC 7613的指南进行密码的规范化处理,具体包括以下步骤:

1. 首先,将所有的非ASCII空格字符映射为ASCII空格字符(U+0020),非ASCII空格字符是指Unicode通用类别为"Zs"的Unicode代码点,但不包括U+0020。

2. 然后,对所有字符应用Unicode规范化形式C(NFC)。

通过这样的处理,即使用户使用不同的输入方法输入相同的密码,密码仍然可以被接受。

“使用不同的输入方法输入相同的密码”的一个例子是,Joe在使用一个全新的北美Mac Book输入密码后离开,然后在一个国际化程度较低的台湾网吧电脑上(Joe正在尝试使用该电脑下载他的登机牌)使用相同的密码。

如果进行了这样的处理,那么还应该验证密码,以拒绝包含尚未分配字符的密码。假设用户使用了应用程序无法识别的NEWFANGLED SPACE,然后你升级了Unicode字符数据库,突然NEWFANGLED SPACE在散列之前被映射为SPACE,这样用户就不能再输入能够散列为旧散列的密码了。

如果应用程序和数据库都设置为全程使用UTF-8,那么为什么还需要额外的映射?因为当你在一台机器上按下空格键,在另一台机器上按下它时,你可能会得到两个不同的Unicode代码点,它们将具有两个不同的UTF-8编码,而用户对此毫不知情。可以说这是一个你希望忽视的问题,但RFC 7613是基于这样的实际问题产生的,它不是一个无用的建议。

一旦决定以某种方式处理密码,就必须始终保持这种处理方式,否则会破坏现有的使用情况。如果将来打算更改预处理方法,应该将其与预处理和散列表示一起存储。这样,一旦接收到输入,可以根据要比较的内容选择预处理/散列方法。

在分析了提议的密码中的非分配字符之后,如果包含了这些字符,您的处理方法是拒绝该密码并向用户显示错误消息,说明他们不能在密码中使用某些字符(我会告诉他们在尝试中找到的非分配字符)。我将尝试找到关于未分配字符的资源,并试图确定这可能对用户体验产生什么影响。

0
0 Comments

(Cleansing User Passwords)这个问题的出现的原因是因为在使用PHP的password_hash()函数对密码进行哈希处理时,不需要进行任何转义、修剪或其他清洗机制。这是因为对密码进行额外的清洗需要额外的代码,而这是不必要的。

解决方法是不要对密码进行任何额外的清洗操作,因为哈希密码不会有任何SQL注入的威胁,原因是字符串在存储到数据库之前已经被转换成哈希。

哈希密码的作用是使密码在数据库中安全存储。哈希函数对任何字节都没有特殊含义,因此不需要对其输入进行清洗以提高安全性。

如果遵循允许用户使用其所需的密码/短语并且不限制密码的规则,允许任意长度、任意数量的空格和任何特殊字符,哈希处理将使密码/短语无论其内容如何都变得安全。当前最常见的哈希方法(默认)是PASSWORD_BCRYPT,它将密码转换为一个包含随机盐、哈希密码信息和成本(创建哈希的算法成本)的60个字符宽的字符串。

对于存储哈希的空间要求可能会因为添加不同的哈希方法到函数中而改变,所以最好在存储哈希的列类型上选择更大的值,例如VARCHAR(255)或TEXT。

可以使用完整的SQL查询作为密码进行哈希处理,这样可以使其无法被SQL引擎执行。

对密码进行修剪等操作会导致在验证密码时出现问题。如果在哈希处理密码之前使用了这些方法,那么在与password_verify()进行比较之前必须再次使用这些方法。这是一个不必要的步骤,并且不会提高哈希的安全性。

在使用PHP版本低于5.5的情况下,可以使用password_hash()函数的兼容包。

不应该使用MD5密码哈希。

如果使用包含尾随空格的密码进行哈希处理,并且在下一次尝试进行身份验证时,用户没有包含这些空格,他还能够访问吗?答案是否定的,如果用户在创建密码时包含了尾随空格(允许的情况下),那么在登录时必须使用这些空格。

如果我们允许用户设置他们想要的密码,包括前导/尾随空格,那么你的建议在可用性方面有一个重大缺点。但是,大多数情况下都会要求用户输入密码两次。如果用户在密码中意外添加了空格,他们在进一步操作之前会发现这个问题。如果用户是故意这样做的,那么这个问题就不存在了。

对于密码,规则是根据业务需求进行清洗,并对进入数据库的所有内容进行转义。这是因为没有人知道未来事物会如何变化。至于密码清洗,这是一个热门话题。例如,Google会对密码进行修剪,但我个人不喜欢这样做。但是他们的帮助中心肯定会处理比我更多的愤怒用户。

规则是一种经验法则。有时我们仍然需要仔细思考,比如对于密码。你说“没有人知道未来事物会如何变化”,但似乎如果有什么东西会发生变化,那就是在将数据放入数据库之前我们如何转义数据的方式,这种情况下,当密码不再与我们存储的内容相匹配时,用户将发现自己被锁定在外面。不转义密码哈希的危险是什么?与对其进行转义的危险相比如何?

也许在PHP世界中情况不同,但在Java世界中,经验法则不是对将要进入数据库的所有内容进行转义,而是使用预编译语句。所以规则也有例外。

确切地说,当然会在正确将其传递给参数化SQL查询时“转义哈希”,在这种情况下,你的SQL连接器中的一些代码可能会对其进行任何与“转义”相对应的操作,你不需要知道也不需要关心。你只需要在所有的SQL查询中进行这一常规操作,除非你之前做出了一些糟糕的决定,否则不需要编写任何特定的代码。

PHP世界正在努力实现预编译语句的经验法则,但较旧的数据库API在野外广泛使用并且已经促进了不良习惯。

关于filter_input(),我个人会过滤密码输入,只是为了去除ASCII < 32的字符。

我会在哈希和验证的时候进行过滤。但是即使这样做也不应该,因为哈希处理会使密码变得安全。

我会对来自HTML表单的原始用户输入进行过滤,例如$password=filter_input(INPUT_POST,"password",FILTER_UNSAFE_RAW,FILTER_FLAG_STRIP_LOW),它会“去除ASCII值小于32的字符”。然后,它会被PHP处理(哈希、验证)并传递给mysqli_statement。所以是的,我只应用了一次过滤,只是去除了ASCII值小于32的字符,比如NULL、[TAB]、[New line]等。

为了防止“请注意,password_hash将在第一个NULL字节处截断密码。”的情况发生,需要对密码中的NULL字节进行处理。

好的观点,但是一个密码中有多少次输入NULL字节的机会?似乎这种情况的可能性非常低,因为有人会在密码中指定八进制字符。

我不知道,可能是因为外语的字符编码、配置错误的机器、有缺陷的硬件,或者只是像在Windows上的ALT-255一样。或者一些奇怪的字符串,比如%00。真的不知道。但是我就是这么多疑(或者有强迫症)的人。另一方面,如果有人确实使用了那些低ASCII值,为了防止NULL传递的几乎不可能的机会,为什么不将它们去除呢。谢谢你的回答。

我将进行一些测试,如果有必要,我将更新答案并提供这些信息。

我在fiddle上进行了一些测试,测试了你的字符串和用我的法语键盘制作的其他随机字符串(±@£¢¤£¦¬¬¤³¦²½³½¾§¶[]}{~l­¯µ),并成功地使用filter_input()从哈希处理和验证中得到了密码。不知道如何或使用什么工具可以发送空字节,所以我没有发布我的结果。似乎除了filter_input()之外,其他大多数函数在比较“RAW”时都失败了。我在一个沙盒中转储了代码 => sandbox.onlinephpfunctions.com/code/47ebec3e5001174d3a58041ae5a4a3d955bb4ff6,并可以在phpfiddle上运行它。问候。

问题出现了,如果我们在第2行和第3行之间添加了$_POST['upassword']="01234567 ",那么我们就会得到一些有趣的行为(来自有趣的输入)。大多数函数返回“Good”,但是trim因为尾随空格的原因失败了,而filter_input则变成了NULL。也许我的测试方法不正确或不准确,但似乎我的测试结论是使用filter_input()更糟糕。现在我们仍然需要一种方法来防止NULL字节传递到password_hash()。

我修剪用户数据,包括密码。所以在阅读这个讨论和答案之后,我认为我不应该修剪密码。我在这里阅读了你的文章http://www.jayblanchard.net/proper_password_hashing_with_PHP.html,这是一个非常好理解的password_hash()和password_verify()的解释。请分享更多这样的易懂的文章。这是我找到的关于password_hash()和password_verify()的最好的解释。非常感谢你。

0