在数据库中加密/散列纯文本密码

我已经继承了一个我刚刚发现的Web应用程序,它以纯文本形式在SQL Server数据库中存储了超过300,000个用户名/密码。 我意识到这是一件非常糟糕的事情。

知道我必须更新登录名和密码更新流程才能进行加密/解密,并且对系统其余部分的影响最小,那么作为从数据库中删除纯文本密码的最佳方式,您会推荐什么?

任何帮助表示赞赏。

编辑:对不起,如果我不清楚,我的意思是问什么是你的程序来加密/散列密码,而不是具体的加密/散列方法。

我应该只是:

  • 备份数据库
  • 更新登录/更新密码
  • 几个小时后,查看用户表中散列密码的所有记录并替换每个记录
  • 测试以确保用户仍可以登录/更新密码
  • 我想我的关注更多来自绝对数量的用户,所以我想确保我正确地做到了这一点。


    我会想象你将不得不为数据库添加一列加密的密码,然后对获取当前密码的所有记录运行一个批处理作业,对其进行加密(如其他人提到的像md5这样的散列是非常标准的编辑:但不应该单独使用 - 请参阅其他答案进行良好的讨论 ),将其存储在新列中,并检查一切是否顺利。

    然后,您需要更新前端以在登录时散列用户输入的密码,并验证存储的散列,而不是检查纯文本与纯文本。

    对于我而言,为了确保在最终除去明文密码之前没有发生任何异议,我将两栏留在原地似乎是明智之举。

    不要忘记,只要密码已经存入,代码将不得不改变,例如密码更改/提醒请求。 你当然会失去发送被遗忘密码的能力,但这不是坏事。 您将不得不使用密码重置系统。

    编辑:最后一点,您可能想考虑避免我在测试平台安全登录网站进行第一次尝试时所犯的错误:

    处理用户密码时,请考虑发生散列的位置。 在我的情况下,哈希是通过运行在网络服务器上的PHP代码计算出来的,但是密码是以纯文本的形式从用户的机器传输到页面的! 在我工作的环境中,这是好的(ish),因为它在https系统内部(uni网络)。 但是,在现实世界中,我想你会希望在密码离开用户系统之前使用javascript等来散列密码,然后将散列传送到您的网站。


    编辑(2016):按照优先顺序使用Argon2,scrypt,bcrypt或PBKDF2。 根据您的情况使用尽可能大的减速因子。 使用经过审查的现有实施。 确保你使用了合适的盐(尽管你使用的库应该为你确保这一点)。


    在散列密码时, 请勿使用普通MD5

    使用PBKDF2,这基本上意味着使用随机盐来防止彩虹表攻击,并且迭代(重新哈希)足够的时间来减慢哈希 - 而不是太多,以至于您的应用程序需要很长时间,但足以让攻击者蛮力大量不同的密码会通知

    从文件:

  • 迭代至少1000次,优选更多的时间执行,以查看可以为您执行多少次迭代。
  • 8字节(64位)的盐是足够的,并且随机不需要是安全的(盐是未加密的,我们并不担心有人会猜测它)。
  • 散列时应用salt的一个好方法是将HMAC与您最喜欢的散列算法一起使用,使用密码作为HMAC密钥,并使用salt作为散列文本(请参阅本文档的这一部分)。
  • 在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
    

    基本策略是使用密钥派生函数将密码与某些salt“散列”。 salt和散列结果存储在数据库中。 当用户输入密码时,salt和他们的输入将以相同的方式进行散列并与存储的值进行比较。 如果它们匹配,则用户被认证。

    细节决定成败。 首先,很多取决于所选择的散列算法。 像PBKDF2这样的密钥派生算法,基于基于散列的消息认证码,使得它在计算上不可行,以找到将产生给定输出的输入(在这种情况下为密码)(攻击者在数据库中发现了什么)。

    预先计算的字典攻击使用预先计算的索引或字典,从散列输出到密码。 散列速度很慢(或者应该是这样),所以攻击者会散列所有可能的密码一次,并将结果索引存储为给定散列的方式,他可以查找相应的密码。 这是时间空间的经典折衷。 由于密码列表可能很大,因此可以调整权衡(如彩虹表),以便攻击者可以放弃一些速度来节省大量空间。

    预计算攻击通过使用“密码盐”来阻止。 这是一些用密码散列的数据。 它不需要是一个秘密,只需要给定密码不可预知。 对于盐的每个值,攻击者都需要一个新的字典。 如果使用一个字节的盐,攻击者需要256个字典副本,每个副本都使用不同的盐生成。 首先,他会使用盐来查找正确的字典,然后他会使用散列输出来查找可用的密码。 但是如果你添加4个字节呢? 现在他需要40亿本字典。 通过使用足够大的盐,词典攻击被排除。 实际上,来自密码质量随机数发生器的8至16字节的数据产生良好的盐。

    通过预先计算表,攻击者在每次尝试时计算散列值。 现在需要多长时间才能找到密码,完全取决于候选人需要多长时间才能找到密码。 这个时间通过迭代散列函数而增加。 数字迭代通常是密钥导出函数的参数; 今天,许多移动设备使用10,000到20,000次迭代,而服务器可能使用100,000次或更多次。 (bcrypt算法使用术语“成本因子”,它是所需时间的对数度量。)

    链接地址: http://www.djcxy.com/p/21537.html

    上一篇: Encrypting/Hashing plain text passwords in database

    下一篇: Best Practices: Salting & peppering passwords?