编辑(2016):按优先顺序使用Argon2、scrypt、bcrypt或PBKDF2。在您的情况下使用尽可能大的减速因子。使用经过审查的现有实施。确保使用适当的盐(尽管您使用的库应该为您确保这一点)。
当您散列密码时,请使用DO NOT USE PLAIN MD5。
使用PBKDF2,这基本上意味着使用随机盐来防止彩虹表攻击,并迭代(重新散列)足够多的时间来减慢散列速度 - 不是你的应用程序花费太长时间,而是足以让攻击者暴力破解大量不同的密码会通知
从文件:
- 迭代至少 1000 次,最好更多 - 为你的实现计时,看看有多少迭代对你来说是可行的。
- 8 字节(64 位)的 salt 就足够了,而且随机数不需要是安全的(salt 是未加密的,我们不担心有人会猜到它)。
- 在散列时应用盐的一个好方法是将 HMAC 与您最喜欢的散列算法一起使用,使用密码作为 HMAC 密钥,盐作为要散列的文本(请参阅文档的这一部分)。
Python 中的示例实现,使用 SHA-256 作为安全哈希:
编辑:正如 Eli Collins 所述,这不是 PBKDF2 实现。您应该更喜欢遵循标准的实现,例如PassLib。
from hashlib import sha256
from hmac import HMAC
import random
def random_bytes(num_bytes):
return "".join(chr(random.randrange(256)) for i in xrange(num_bytes))
def pbkdf_sha256(password, salt, iterations):
result = password
for i in xrange(iterations):
result = HMAC(result, salt, sha256).digest() # use HMAC to apply the salt
return result
NUM_ITERATIONS = 5000
def hash_password(plain_password):
salt = random_bytes(8) # 64 bits
hashed_password = pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)
# return the salt and hashed password, encoded in base64 and split with ","
return salt.encode("base64").strip() + "," + hashed_password.encode("base64").strip()
def check_password(saved_password_entry, plain_password):
salt, hashed_password = saved_password_entry.split(",")
salt = salt.decode("base64")
hashed_password = hashed_password.decode("base64")
return hashed_password == pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)
password_entry = hash_password("mysecret")
print password_entry # will print, for example: 8Y1ZO8Y1pi4=,r7Acg5iRiZ/x4QwFLhPMjASESxesoIcdJRSDkqWYfaA=
check_password(password_entry, "mysecret") # returns True