我继承了我在一个SQL Server数据库的纯文本只是发现商店超过30万名的用户名/密码的web应用程序。 我意识到这是一个非常糟糕的事情™。
知道我将不得不更新的登录名和密码更新过程进行加密/解密,并与系统其余部分的影响最小,你有什么建议,以从数据库中删除纯文本密码的最佳方式?
任何帮助表示赞赏。
编辑:对不起,如果我不清楚,我的意思是问什么是你的程序加密/哈希密码,而不是特定的加密/散列方法。
我应该:
- 使数据库的备份
- 更新登录/更新密码的代码
- 几个小时后,经过users表中的所有记录散列密码和更换每一个
- 进行测试以确保用户仍然可以登录/密码更新
我想我的问题是从用户的绝对数量多,所以我想确保我正确地做这个。
Answer 1:
我猜想,你将有一列添加到数据库的加密密码,然后运行在它获取的当前密码的所有记录批处理作业,进行加密(如其他人mentiond如MD5哈希是非常标准的编辑:但不应该对自己使用-请参见良好的讨论其他的答案 ),将其存储在新列,并检查这一切发生的顺利进行。
然后,你将需要更新您的前端凑在登录时的用户输入的密码和验证VS存储的哈希,而不是检查明文-VS-明文。
这似乎是明智的我留在原地两列了一小会儿,以确保没有hinky已经上,最终消除明文密码全在一起之前。
也不要忘了随时密码acessed代码将不得不改变,如密码更改/提醒请求。 当然,你就失去了电子邮件out忘记的密码的能力,但是,这不是坏事。 你将不得不使用密码重置系统来代替。
编辑:最后一点,你可能要考虑避免我对我在一个试验台安全登录网站的第一次尝试做出的错误:
在处理用户密码,考虑散列发生。 在我的情况下,哈希是由在Web服务器上运行PHP代码计算的,但密码是明文传输到页从用户的机器! 这是我工作的环境OK(ISH),因为它是一个系统的https无论如何(单向网络)内。 但是,在现实世界中我想你会希望哈希密码在离开之前,用户系统,使用JavaScript等,然后传送散列到您的网站。
Answer 2:
编辑(2016):使用Argon2 , scrypt , bcrypt ,或PBKDF2 ,在优先顺序。 如您的具体情况可行使用尽可能大的减速因素。 使用审核现有的实现。 请确保您使用合适的盐(虽然库,你正在使用,应确保为你处理)。
当你哈希密码使用时, 请勿使用纯MD5。
使用PBKDF2 ,基本上是指使用随机盐,以防止彩虹表攻击,迭代(重新哈希)足够的时间,以减缓散列下来-没有这么多,你的应用程序时间太长,但足以使攻击者暴力破解一个大量不同的密码会注意到
从文件:
- 迭代至少1000倍,最好更多 - 时间您的实现,看看有多少次迭代你是可行的。
- 盐的8个字节(64位)是足够的,和随机并不需要是安全的(盐是未加密的,我们并不担心会有人猜测它)。
- 申请散列当盐的一个好方法是使用HMAC与您喜爱的散列算法,使用密码作为HMAC密钥和加盐作为哈希文(见本节文件的)。
在Python示例实现,使用SHA-256作为安全散列:
编辑 :作为由礼科林斯提到这不是一个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
Answer 3:
其基本策略是使用一个密钥导出函数为“哈希”用少许盐密码。 盐和散列结果被存储在数据库中。 当用户输入一个密码,将盐和它们的输入被散列以相同的方式,并与所存储的值。 如果它们匹配,用户进行身份验证。
细节决定成败。 首先,很多取决于选择的散列算法。 密钥导出算法像PBKDF2的基础上,基于散列的消息认证码,使得“计算上不可行”,找到(在这种情况下,密码),将产生一个给定输出(什么攻击者在数据库中发现输入)。
甲预先计算的字典攻击使用预先计算的索引,或字典,从散列输出到密码。 散列缓慢(或者它应该是,无论如何),所以攻击哈希所有可能的密码一次,并存储在给定一个哈希这样索引的结果,他可以查找相应的密码。 这是空间换时间的经典折衷。 由于密码列表可以是巨大的,有办法调整权衡(如彩虹表),从而使攻击者可以放弃一点速度,节省了大量的空间。
预先计算攻击通过使用“加密盐”挫败。 这是一些数据散列与密码。 它并不需要是一个秘密,它只是需要一个给定的密码是不可预知的。 对于盐的每个值,攻击者需要一个新的字典。 如果您使用的盐的一个字节,攻击者需要他们的字典,每一个不同的盐产生的256份。 首先,他会使用盐查找正确的词典,那么他会使用哈希输出来查找一个可用的密码。 但是如果你添加4个字节? 现在他需要的字典4十亿份。 通过使用一个足够大的盐,字典攻击被排除。 在实践中,从加密质量随机数发生器8至16字节的数据使一个良好的盐。
随着预先计算过的表,攻击者必须计算每次尝试的哈希值。 需要多长时间找到一个密码现在完全取决于需要多长时间散列的候选人。 这一次增加散列函数的迭代。 的迭代次数一般为密钥推导函数的参数; 今天,很多移动设备的使用10000至20000反复,而一台服务器可以使用10万以上。 (该bcrypt算法使用的术语“成本因素”,这是所需要的时间的对数测量值。)
Answer 4:
按照克桑的建议各地保持当前密码列了一段时间的,所以如果事情坏了,你可以回滚快速正方便。
至于加密您的密码:
- 使用盐
- 使用的意思了密码的哈希算法(也就是-它是慢 )
参见Thomas Ptacek的足以与彩虹表:你需要知道的关于安全密码方案的一些细节。
Answer 5:
我想你应该做到以下几点:
- 创建一个名为HASHED_PASSWORD或类似的东西新列。
- 修改您的代码,以便它检查两个列。
- 逐渐迁移从非哈希表密码散列之一。 例如,当用户登录时,会自动迁移他或她的密码哈希列和删除散列的版本。 所有新注册用户将有哈希密码。
- 几个小时后,你可以运行一个脚本迁移N个用户时
- 当你有没有留下更多的非散列密码,您可以删除旧密码栏(你可能无法做到这一点,取决于你所使用的数据库)。 此外,您还可以删除代码来处理旧密码。
- 你完成了!
Answer 6:
这是我几个星期的问题前。 我们在部署一个大型MIS项目到我们自己的用户证书存储将被用作不同的一组已实施的认证和使用的应用程序975个不同的地理位置。 我们已经提供了REST和SOAP的身份验证服务,但客户坚持能够达到从其他应用程序的用户证书存储只有AA DB连接设置为只读相关的表或视图的视图。 唉...(这一高度耦合的糟糕的设计决策是另外一个问题一个主题)。
这迫使我们坐下来,我们的盐渍和反复哈希密码存储方案转变为规范和易于集成提供了一些不同的语言实现。
我们把它叫做相当安全哈希密码或FSHP短。 实现了它在Python和Ruby,PHP5和它发布到公共领域。 可供食用,分叉,火烧或吐在GitHub上http://github.com/bdd/fshp
FSHP是腌,反复哈希密码散列实现。
设计原理是类似的PBKDF1在RFC 2898规范(又名:PKCS#5:基于密码的加密规范版本2.0。)FSHP允许选择之间的盐长度,迭代次数和底层密码散列函数SHA-1和SHA-2 (256,384,512)。 自我在每个输出的开头定义元前缀使得便携的同时,让消费者选择自己的密码存储的安全基准。
安全性 :
默认FSHP1使用8克字节的盐,与SHA-256哈希的4096次迭代。 - 8字节盐呈现由所需要的空间用2 ^ 64乘以不切实际彩虹表攻击。 - 4096次迭代使蛮力攻击是相当昂贵的。 - 有对SHA-256没有已知的攻击,找到这个版本的时间少于2 ^ 128操作的计算工作量的碰撞。
实现:
- Python的:与2.3.5(W / hashlib)测试,2.5.1,2.6.1
- 红宝石:测试了1.8.6
- PHP5:与5.2.6测试
每个人都非常欢迎建立失踪语言实现或抛光当前那些多。
基本操作(使用Python):
>>> fsh = fshp.crypt('OrpheanBeholderScryDoubt')
>>> print fsh
{FSHP1|8|4096}GVSUFDAjdh0vBosn1GUhzGLHP7BmkbCZVH/3TQqGIjADXpc+6NCg3g==
>>> fshp.validate('OrpheanBeholderScryDoubt', fsh)
True
自定义CRYPT:
让我们削弱我们的密码散列方案。 - 减少从默认8所述盐长度为2 - 减少从默认4096迭代轮10 - 选择FSHP0与SHA-1作为底层散列算法。
>>> fsh = fshp.crypt('ExecuteOrder66', saltlen=2, rounds=10, variant=0)
>>> print fsh
{FSHP0|2|10}Nge7yRT/vueEGVFPIxcDjiaHQGFQaQ==
Answer 7:
正如其他人所说,你不想来解密,如果你能帮助它。 标准的最佳实践是使用单向散列加密,然后当哈希密码的用户日志进行比较。
否则,你将不得不使用强大的加密方法来加密和解密,然后。 我只建议在政治上的原因是强大的(例如,您的用户习惯于能够打电话到服务台取回自己的密码,你必须从上面的强大压力不改变这种状况)。 在这种情况下,我会用加密开始,然后开始建立业务案例转移到哈希。
Answer 8:
对于你应该避免使用可逆加密存储密码认证的目的,即你应该只存储密码的哈希和检查对已存储的散列用户提供密码的哈希值。 然而,这种方法有一个缺点:它的脆弱彩虹表攻击,如果攻击者得到你的密码存储数据库的保持。
你应该做的是收纳预先选定的(和秘密)盐值+密码的哈希值。 即,串联盐和密码,散列结果,并存储该散列。 身份验证时,做同样的 - 串联您的盐价值和用户提供的密码,哈希,然后检查平等。 这使得彩虹表攻击是不可行的。
当然,如果用户发送密码在网络上(例如,如果你工作在一个网页或客户端 - 服务器应用程序),那么你不应该跨越发送明文密码,因此,而不是存储哈希(盐+密码),你应该存储和核对哈希(盐+哈希(密码)),和你的客户预哈希用户提供的密码,并通过网络发送的那一个。 这将保护您的用户密码,以及,如果用户(因为很多人)用于多种用途重复使用相同的密码。
Answer 9:
- 加密使用类似MD5,其编码为十六进制字符串
- 你需要盐; 你的情况,用户名可以作为盐(它必须是唯一的,用户名应该是可用的最独特的价值;-)
- 使用旧密码字段存储的MD5,但标记的MD5(IEG“MD5:687A878 ....”)使老(纯文本)和新(MD5)密码可以共存
- 改变登录过程验证针对MD5,如果有一个MD5,并抵靠明文口令否则
- 改变“更改密码”和“新用户”功能仅创建MD5'ed密码
- 现在你可以只要在需要运行转换批处理作业,这可能需要
- 转换已运行后,取出遗赠扶养
Answer 10:
第1步:加密字段添加到数据库
第2步:更改代码,以便当密码被更改,它会更新这两个领域,但在登录仍然使用旧场。
第3步:运行脚本来填充所有的新领域。
第4步:更改代码,以便在登录使用的新领域和更改密码停止更新旧领域。
第5步:从数据库中删除未加密的密码。
这应该允许您以完成切换,而不会中断终端用户。
另外:我的东西会做的是命名新的数据库字段的东西是完全无关的,如“LastSessionID”密码或一些类似的无聊。 然后,而不是删除密码字段,只用随机数据的哈希值填充。 然后,如果你的数据库不断被泄露,他们可以花所有的时间,他们想在试图解密“密码”字段。
这实际上可能没有完成任何事情,但它的乐趣想着有人坐在那里试图找出毫无价值的信息
Answer 11:
如同所有的安全决策,也有权衡。 如果散列密码,这可能是最简单的举动,你不能提供恢复原来的密码的密码检索功能,也不能将你的员工才能访问他们的账户中查找一个人的密码。
您可以使用对称加密,它有其自身的安全缺陷。 (如果您的服务器被攻破,对称加密密钥也可以被破坏)。
您可以使用公钥加密,和一个单独的机器,存储从Web应用程序隔离私钥上运行密码检索/客户服务。 这是最安全的,但需要一个双机架构,并在可能之间有防火墙。
Answer 12:
我不是一个安全专家,但我htink目前的建议是使用bcrypt /河豚或SHA-2变种,而不是MD5 / SHA1。
也许你需要考虑在一个完整的安全审计方面,也
Answer 13:
MD5和SHA1都表现出了位弱点的(可导致相同的哈希两个单词),从而使用SHA256-SHA512 /迭代散列建议散列的口令。
我会写在编写应用程序在云和生成一个随机的盐是每个用户和密码的哈希独特的语言的小程序。 我倾向于使用相同的语言作为核查的原因是,不同的加密库可以这样使用相同的库,生成散列并验证它消除了风险做的事情略有不同(即填充)。 这个应用程序还可以再确认表后,登录已被更新,如果你想因为它知道明文密码依然。
- 不要使用MD5 / SHA1
- 产生了良好的随机盐(许多加密库有一个盐发电机)
- 推荐的迭代散列算法为OriP位
- 确保密码以纯文本通过线路传输不
Answer 14:
我想提出一个改进张贴OriP的大蟒蛇例子 。 我会重新定义random_bytes
功能是:
def random_bytes(num_bytes):
return os.urandom(num_bytes)
当然,你将不得不进口os
模块。 所述os.urandom
功能提供了可在加密应用程序可以安全地使用的字节的随机序列。 见这个功能的参考帮助了解更多详情。
Answer 15:
为了哈希密码,你可以使用HASHBYTES功能。 返回VARBINARY,所以你必须创建一个新列,然后删除旧的varchar之一。
喜欢
ALTER TABLE users ADD COLUMN hashedPassword varbinary(max);
ALTER TABLE users ADD COLUMN salt char(10);
--Generate random salts and update the column, after that
UPDATE users SET hashedPassword = HashBytes('SHA1',salt + '|' + password);
然后你修改代码以验证密码,使用类似的查询
SELECT count(*) from users WHERE hashedPassword =
HashBytes('SHA1',salt + '|' + <password>)
其中<密码>是由用户输入的值。
Answer 16:
文章来源: Encrypting/Hashing plain text passwords in database