OSX中的python crypt

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

OS X:

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

Linux的:

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

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

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

我怎样才能让OS X的crypt()实现生成与Linux crypt()相同的结果?
为什么不是Python实现所涵盖的(正如我期望从这种情况进行跨平台部署)?

这是因为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机器上生成的原始文件相同。

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

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

在你的python示例中,你告诉crypt使用id为1,这会导致使用MD5而不是基于DES的散列。 在Mac OS X上没有这样的扩展,其中crypt严格基于DES。 (Mac OS X的crypt有自己的扩展 – salt可以是9个字符的数组,以下划线开头,后跟4个字节的迭代计数和4个字节的盐 – 在glibc的实现中没有模拟。)

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

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

从安全角度来看,这显然很糟糕。 考虑使用像passlib或py-bcrypt这样的东西。 任何一个都可以同时为您提供更好的散列和跨平台可靠性。

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

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

您可以编写一些内容,或者找到一个模块,重新实现crypt在每个环境中使用的哈希算法 – 但同样,这会破坏跨平台的目的。 您将硬编码您的应用程序以使用Ubunutu,它可能使用不同的隐藏不仅来自OSX,而​​且来自其他Unix和BSD风格,如RedHat,FreeBSD等。