12

我有一个 Django 应用程序,它重置在 Ubuntu 机器上运行的 unix 用户密码,但我的开发环境是 OS X,我遇到了这种烦人的情况:

操作系统:

>>> import crypt
>>> crypt.crypt('test','$1$VFvON1xK$')
'$1SoNol0Ye6Xk'

Linux:

>>> import crypt
>>> crypt.crypt('test','$1$VFvON1xK$')
'$1$VFvON1xK$SboCDZGBieKF1ns2GBfY50'

通过阅读 pydoc crypt,我看到它使用了特定于操作系统的crypt实现,因此我还在两个系统中测试了以下代码,结果与 Python 相同:

#include <unistd.h>

int main() {
        char *des = crypt("test","$1$VFvON1xK$ls4Zz4XTEuVI.1PnYm28.1");
        puts(des);
}

如何让 OS X 的crypt()实现产生与 Linux 相同的结果crypt()
为什么 Python 实现没有涵盖这一点(正如我对跨平台部署的这种情况所期望的那样)?

4

4 回答 4

15

这是因为 Linux 的 glibc 处理密码的方式不同 - Linux 上密码的盐值对应于它生成的散列类型。OSX crypt() 是普通的 DES 加密,(这很可怕)。

glibc 支持多种哈希算法(MD5、Blowfish、SHA-256 等)。

如果我们查看crypt.3联机帮助页,我们可以看到:

   If salt is a character string starting with the characters "$id$" followed by
   a string terminated by "$":

          $id$salt$encrypted

   then instead of using the DES machine, id identifies the encryption method
   used and this then determines how the rest of the password string is
   interpreted.  The following values of id are supported:

          ID  | Method
          ---------------------------------------------------------
          1   | MD5
          2a  | Blowfish (not in mainline glibc; added in some
              | Linux distributions)
          5   | SHA-256 (since glibc 2.7)
          6   | SHA-512 (since glibc 2.7)

因此,鉴于这些信息.. 让我们使用 Linux 的 crypt 从第二个示例中获取您的密码

$1$VFvON1xK$SboCDZGBieKF1ns2GBfY50' ('test', encrypted with salt=VFvON1xK)


1                       == MD5
VFvON1xK                == Salt
SboCDZGBieKF1ns2GBfY50  == Hashed password

幸运的是,有一个跨平台的解决方案passlib.hash.md5_crypt

下面是你如何使用它:

from passlib.hash import md5_crypt
hash = md5_crypt.encrypt("test",salt="VFvON1xK")
print hash

在 Linux 或 OSX 上运行时,生成以下 glibc 友好密码哈希:

$1$VFvON1xK$SboCDZGBieKF1ns2GBfY50

与在 Linux 机器上生成的原始版本相同。

于 2012-10-24T15:59:29.520 回答
5

您正在将专门的盐字符串传递给调用特定于 glibc 的 crypt 行为的函数,这些行为在 Mac OS X 上不可用。来自 Debian 6 上的 crypt(3) 手册页:

如果 salt 是一个以字符“$id$”开头的字符串,后跟一个以“$”结尾的字符串......那么 id 不使用 DES 机器,而是标识使用的加密方法,然后确定其余部分如何密码字符串被解释。

在您的 python 示例中,您告诉 crypt 使用id1,这会导致使用 M​​D5 而不是基于 DES 的散列。crypt在严格基于 DES 的Mac OS X 上没有这样的扩展。(Mac OS Xcrypt有它自己的扩展——salt 可以是一个 9 字符的数组,以下划线开头,后跟 4 个字节的迭代计数和 4 个字节的 salt——在 glibc 的实现中没有类似物。)

如果您避免crypt在两个平台上进行扩展并使用传统crypt的 salt ,其中 salt 只能是两个字节,您将从两个平台上的函数中获得相同的结果,例如:

>>> crypt.crypt( "test", "S/" )
'S/AOO.b04HTR6'

从安全的角度来看,这显然很糟糕。考虑改用passlibpy-bcrypt之类的东西。任何一个都可以同时为您带来更好的散列和跨平台可靠性。

于 2012-10-24T16:03:10.207 回答
2

为什么要在 Python 中使用单个 crypt 函数?如果您在 OSX 中运行,则需要 osx 版本的 crypt(),如果您在 ubuntu 中运行,它将使用 ubuntu 的 crypt()。

这是一个跨平台的解决方案——Python 使用 OS crypt 来确保环境中的兼容性。如果 Python 使用它自己的 crypt(),那么哈希值将是相同的——但它可以在 OSX 而不是 Ubuntu 上工作(反之亦然)

您可以编写一些东西或找到一个模块,以重新实现 crypt 在每个环境中使用的散列算法——但同样,这会破坏跨平台的目的。您将硬编码您的应用程序以在 Ubunutu 上工作,它可能使用不同的密码,不仅来自 OSX,而​​且来自其他 Unix 和 BSD 风格,如 RedHat、FreeBSD 等。

于 2012-10-24T15:29:19.400 回答
1

作为对@synthesizerpatel答案的更新,该.encrypt()方法现在在 passlib 中已弃用,并由该.hash()方法替换:

from passlib.hash import md5_crypt
hash = md5_crypt.hash("test", salt="VFvON1xK")
print(hash)
于 2022-01-14T15:23:07.283 回答