我正在编写一个快速的REST服务来管理我们域的电子邮件帐户,并遇到了一种我无法解释的奇怪行为。
我正在使用mysql来验证电子邮件帐户,我们的REST服务使用Web界面管理maildirs并插入或更新身份验证凭据。 用于插入用户以及更新其密码的查询都使用 encrypt
mysql 命令。
奇怪的是,插入的用户在 REST 接口上的密码至少更改一次之前不会进行身份验证。 这意味着encrypt
为更新设置了正确的值,但插入设置了错误。
我尝试在其余服务端的 GET/POST 参数中记录所有内容,但数据库层似乎出了问题。 REST 服务在两端记录密码的正确参数。 查询执行中的某些内容是不同的。
我的查询看起来像这样(在Python中使用MySQLdb(:
ADD_USER = "INSERT INTO users (id,name,maildir,crypt) VALUES (%s,%s,%s,encrypt(%s));"
CHANGE_PASS = "UPDATE users SET crypt = encrypt(%s) WHERE id=%s"
同样,ADD_USER
输入了一个错误的哈希,其中CHANGE_PASS
成功使用完全相同的 HTTP 参数crypt
传递给它们(作为字符串(。 数据层会执行不同的加密是否有任何原因,或者我吠错了树?
如果你使用不加盐的加密,它将使用基于当前时间的盐,所以每次调用它时,你都会得到一个不同的哈希:
mysql> select encrypt('test');
+-----------------+
| encrypt('test') |
+-----------------+
| 92SErC2PadiaQ |
+-----------------+
mysql> select encrypt('test');
+-----------------+
| encrypt('test') |
+-----------------+
| A2jgxXgOJx7ls |
+-----------------+
前两个字符(在标准情况下(是盐。 如果要检查它,请使用旧密码作为盐,除前两个字符外的所有字符都将被忽略:
mysql> select encrypt('test', 'A2jgxXgOJx7ls');
+----------------------------------+
| encrypt('test', 'A2jgxXgOJx7ls') |
+----------------------------------+
| A2jgxXgOJx7ls |
+----------------------------------+
如果你想要更强的密码,你必须使用一个特殊的盐:
mysql> select encrypt('test', '$1$12345678$') as md5;
+------------------------------------+
| md5 |
+------------------------------------+
| $1$12345678$oEitTZYQtRHfNGmsFvTBA/ |
+------------------------------------+
mysql> select encrypt('test', '$5$0123456789abcdef$') sha256;
+-----------------------------------------------------------------+
| sha256 |
+-----------------------------------------------------------------+
| $5$0123456789abcdef$Wm4jf6bGxEoelzY0H/fTvcw8Qcshq0hyLaRfZWtN8q. |
+-----------------------------------------------------------------+
mysql> select encrypt('test', '$6$0123456789abcdef$') as sha512;
+------------------------------------------------------------------------------------------------------------+
| sha512 |
+------------------------------------------------------------------------------------------------------------+
| $6$0123456789abcdef$vNATSYYTivQfXwPTUT4q.sRFLs/sgxDXaPipzRlX3WOO4r1NcR.Og5OoU2Cd2agm1WA3pCJ30JU4EKMxpZaDy/ |
+------------------------------------------------------------------------------------------------------------+
所以这完全取决于你用什么盐。 切勿使用无盐哈希。
首先:如果可以的话,不要使用ENCRYPT()
作为密码。它有点有限(例如:它忽略密码前 8 个字符以外的所有内容(,而且它不是特别安全,特别是因为这意味着 MySQL 查询日志记录将记录用户的密码!你最好在Python中进行密码哈希 - 这意味着MySQL不仅不必知道任何关于实际密码的信息,而且还允许您使用更强大的密码哈希算法。
话虽如此,请记住,ENCRYPT()
不是纯粹的哈希函数。它的结果不是恒定的,除非你传入盐作为第二个参数。您如何检查密码?正确的解决方案如下所示:
SELECT * FROM users WHERE <...> AND crypt = ENCRYPT(%s, crypt) ...
或者,在 Python 中:
row.crypt == crypt.crypt(users_input_password, row.crypt)