2012-08-06 143 views
3

我用光彦的实现PBKDF2的密码散列:PBKDF2和哈希比较

def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): 
    """Returns a binary digest for the PBKDF2 hash algorithm of `data` 
    with the given `salt`. It iterates `iterations` time and produces a 
    key of `keylen` bytes. By default SHA-1 is used as hash function, 
    a different hashlib `hashfunc` can be provided. 
    """ 
    hashfunc = hashfunc or hashlib.sha1 
    mac = hmac.new(data, None, hashfunc) 
    def _pseudorandom(x, mac=mac): 
     h = mac.copy() 
     h.update(x) 
     return map(ord, h.digest()) 
    buf = [] 
    for block in xrange(1, -(-keylen // mac.digest_size) + 1): 
     rv = u = _pseudorandom(salt + _pack_int(block)) 
     for i in xrange(iterations - 1): 
      u = _pseudorandom(''.join(map(chr, u))) 
      rv = starmap(xor, izip(rv, u)) 
     buf.extend(rv) 
    return ''.join(map(chr, buf))[:keylen] 

该函数返回二进制消化,然后在base64编码,并保存到数据库中。 。也是的base64字符串设定为当用户登录

此功能用于密码的cookie散列比较:

def comparePasswords(password1, password2): 
    if len(password1) != len(password2): 
     return False 
    diff = 0 
    for x, y in izip(password1, password2): 
     diff |= ord(x)^ord(y) 
    return diff == 0 

我不知道是否有二进制散列和的base64字符串的比较有什么区别在安全方面?例如,当用户登录时,我计算提交的密码的二进制摘要,从数据库中解码base64字符串,然后比较两个二进制哈希值,但如果用户有base64字符串的cookie,我直接将其与来自数据库。

第二个问题是关于盐:

os.urandom返回二进制串,但它是在哈希生成使用之前我也编码它以base64。为什么我不应该这样做,并以二元形式使用盐,是否有任何理由?

+0

对任何一个阅读:这是一个老问题,** PBKDF2不再是推荐的密码哈希算法**(尽管它比许多人们在实践中使用的破碎方案好得多)。如今这个称号属于** Argon2i **,是密码散列竞赛的赢家。 – 2017-10-20 07:39:23

回答

10

要回答的问题1:比较字节VS比较base64编码字符串...你只是比较nn*4/3元素时,有没有重大的安全差异。运行时将不再4/3使用Base64,但时间量仍然微不足道:)

这就是说,有一个Python开发discussion关于类似“固定时间”的比较功能,他们想出了几个VM-如果你的输入是unicode字符串而不是bytes,并且特别是如果unicode包含非ASCII字符,那么可能仍然存在一些微妙的定时攻击(比短路平等攻击小几个数量级)。因此,如果可能的话,我会坚持字节(无论是二进制数据还是ASCII编码的base64数据)。 但是,我不担心在PBKDF2的情况下太多,因为timing attack比较函数被设计为失败更适用于HMAC签名,而不是密码哈希验证......但更好是安全而不是遗憾。

要回答的问题2:对于不安全的结构,如md5(salt+password),第一编码盐将允许攻击者利用现有的ASCII MD5表来攻击整个摘要,其中原始二进制盐会做出这样的表没用。但是,PBKDF2-HMAC做了足够的修改,唯一重要的是盐含有n熵位 - 无论是以n/8原始字节的形式还是n/6 base64字符都不影响安全性。

其他说明: 我只是想补充一些其他的点与你贴什么...

  1. 为了安全起见我强烈建议使用SHA256/512,而不是作为SHA1 PBKDF2-HMAC散列函数,> = 10,000轮(截至2012年),以确保安全。在cookie中发送摘要(甚至不用盐)的想法让我感到潜在的不安全感......如果有人偷了这个cookie(例如,跨站点脚本攻击),他们可能会以用户身份登录(尽管我不清楚应用程序的安全设置)。使用具有随机生成的会话ID的会话层(例如Beaker)可能是一个不错的选择。

  2. 我建议使用Passlib PBKDF2consteq实现,它的PBKDF2例行约5倍比光彦的更快,如果存在的话可以采取的M2Crypto优势。 (免责声明:我是Passlib的作者)。它还有一个现成的pbkdf2-sha256密码散列函数,但如果你要在cookie中发送摘要,这不会有太大的用处。

+0

非常感谢Eli为你的时间和分享你的知识!我留下了深刻的印象,人们在计算器上的反应如何。 我已经使用SHA256,我将迭代次数增加到10000次。此外,我正在看会议,但我决定坚持简单和使用cookie,因为我的应用程序是一个简单的单用户博客,带有匿名阅读器和1个管理员,必须经过身份验证。 – wombatonfire 2012-08-07 07:01:53

+0

unicode字符串有一个有趣的问题。正如我从你提到的讨论中可以理解的,如果我们比较2个unicode字符串,就会出现问题,因为我们需要在比较之前对它们进行编码。我想知道,它会如何影响我?我的输入是来自POST的密码。这是一个unicode字符串,我编码为HMAC工作,然后我将它保存为一个哈希: 'formData = web.input() password = pbkdf2_bin(formData.password.encode('utf-8'),user.salt ,hashfunc = hashlib.sha256)' 我不直接比较unicode字符串,而是比较二进制或base64形式的散列。 – wombatonfire 2012-08-07 07:25:19

+0

unicode计时问题并不适用于您的案例(或大多数密码哈希比较),因为它要求攻击者对'comparePasswords()'的两个输入都有一定程度的控制。回过头来看,我并不是故意要把这些文字写得像我这样做,我想提高对这个缺陷的认识,但是我怀疑我写了太多的东西:) – 2012-08-08 00:01:32