锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

对数据加密

时间:2023-08-05 04:37:00 连接器df1ec

什么是密码哈希?

哈希算法是一种单向函数。它把任意数量的数据转换为固定长度的“指纹”,而且这个过程无法逆转。它们有这样的特性:如果输入发生了一点改变,由此产生的哈希值会完全不同(参见上面的例子)。这个特性很适合用来存储密码。因为我们需要一种不可逆的算法来加密存储的密码,同时保证我们也能够验证用户登陆的密码是否正确。 

如何破解哈希?

字典攻击和暴力攻击( Dictionary and Brute Force Attacks)

破解哈希加密最简单的方法是尝试猜测密码,比较每个猜测的密码,比较猜测密码的哈希值是否等于破解的哈希值。如果相等,猜猜。最常见的两种猜测密码攻击方法是字典攻击和暴力攻击 。

字典攻击使用字典文件,包括单词、短语、常用密码和其他可能用作密码字符串。对文件中的每个单词进行哈希加密,将这些哈希值与要破解的密码哈希值进行比较。假如它们是一样的,这个词就是密码。字典文件由大段文本中提取的单词组成,甚至包括数据库中的一些真实密码。还可以进一步处理字典文件,使其更有效:如单词 “hello” 按网络语写法转换 “h3110” 。

暴力攻击是尝试每个可能的字符组合给定的密码长度。这样会消耗大量的计算,也是破解哈希加密效率最低的方法,但最终会找到正确的密码。码。因此,密码应该足够长,以至于所有可能的字符组合都花费了太长的时间来放弃破解。

目前还没有办法组织字典攻击或暴力攻击。我们只能找到让它们低效的方法。如果密码哈希系统的设计是安全的,解决哈希的唯一方法就是对每个哈希值进行字典攻击或暴力攻击。

查表法( Lookup Tables)

查表法是破解相同类型哈希值的一种非常有效的方法。主要概念是提前计算( pre-compute)发布密码字典中每个密码的哈希值,然后将相应的密码存储在一个表中。一个设计良好的查询表结构,即使包含数十亿个哈希值,仍然可以每秒查询数百个哈希。

反向查表法( Reverse Lookup Tables)

这种攻击允许攻击者对多个哈希值发起字典攻击或暴力攻击,而无需提前计算查询表。

首先,攻击者从黑色用户账户数据库中创建用户名和相应的密码哈希表,然后,攻击者猜测一系列哈希值,并使用查询表找到使用该密码的用户。许多用户通常使用相同的密码,所以这种攻击特别有效。

彩虹表( Rainbow Tables)

彩虹表是一种以空间换时间的技术。类似于查表法,它牺牲了破解速度,以使查询表更小。由于彩虹表较小,可以在单位空间中存储更多的哈希值,从而使攻击更有效。最多可以破解任何8位长度 MD5 彩虹表已经出现。

接下来,让我们来看一个所谓的加盐( salting)该技术可使查表法和彩虹表失效。

加盐( Adding Salt)

hash("hello")                    = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 hash("hello"   "QxLUF1bgIAdeQX") = 9e209040c863f84a31e719795b2577523954739fe5ed3b58a75cff2127075ed1 hash("hello"   "bv5PehSMfV11Cd") = d1d3ec2e6f20fd420d50e2642992841d8338a314b8ea157c9e18477aaef226ab hash("hello"   "YYLmfY6IehjZMQ") = a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2c4a8544305df1b60f007

查表法和彩虹表只有在所有密码完全相同的情况下才有效。如果两个用户有相同的密码,他们将有相同的密码哈希值。我们可以随机哈希,当同一密码哈希两次时,哈希值是不同的,以避免这种攻击。

我们可以通过在密码中添加一个随机字符串来加密哈希,称为盐值。例如,这使得相同的密码每次都被加密成完全不同的字符串。我们需要盐值来检查密码是否正确。它通常与密码哈希值一起存储在帐户数据库中,或作为哈希字符串的一部分。

盐值不需要加密。由于哈希值是随机的,检查表、反向检查表和彩虹表将无效。由于攻击者不能提前知道盐值,所以他们不能提前计算查询表或彩虹表。如果每个用户的密码用不同的盐加密,反向检查表的攻击将无效。

错误的方法:短盐值和盐值重用

最常见的错误是哈希多次加密使用相同的盐值,或者盐值太短。

盐值复用( Salt Reuse)

当用户创建帐户或更改密码时,应使用新的随机盐值进行加密。

短盐值( Short Slat)

为了使攻击者无法构建包含所有可能盐值的查询表,盐值必须足够长。良好的经验是使用与哈希函数输出字符串相同的长盐值。 SHA256 输出为256位(32字节),因此盐也应为32个随机字节。

双哈希和奇怪的哈希函数

奇怪的哈希算法组合。人们很容易感到困惑,尝试结合不同的哈希函数,希望使数据更安全。但在实践中,这并不好。它带来了函数之间的交换问题,甚至可能使哈希更不安全。

当攻击者不知道哈希加密算法时,他们就不能发动攻击。但考虑到柯克霍夫的原则,攻击者通常会获得源代码(特别是免费或开源软件)。通过系统找到密码-哈希值对应关系,很容易反向推导加密算法。

如何正确加密哈希?

基础知识:加盐哈希( Hashing with Salt)

盐值应使用加密安全伪随机数生成器( Cryptographically Secure Pseudo-Random Number Generator,CSPRNG )产生。CSPRNG与普通伪随机数生成器大不相同,CSPRNG 设计用于加密安全,这意味着它可以提供高度随机、完全不可预测的随机数。我们不希望测盐值,所以必须使用 CSPRNG 。下表列出了一些主流编程平台 CSPRNG 方法。

Platform

CSPRNG

PHP

mcrypt_create_iv, openssl_random_pseudo_bytes

Java

java.security.SecureRandom

Dot NET (C#, VB)

System.Security.Cryptography.RNGCryptoServiceProvider

Ruby

SecureRandom

Python

os.urandom

Perl

Math::Random::Secure

C/C (Windows API)

CryptGenRandom

Any language on GNU/Linux or Unix

Read from /dev/random or /dev/urandom

每个用户的每个密码都应该使用独特的盐值。每次用户创建帐户或更改密码时,密码应使用新的随机盐值。永远不要重复使用某个盐值。盐值也应该足够长,以便有足够的盐值用于哈希加密。一个经验规则是盐值至少应该和哈希函数的输出一样长。盐应与密码哈希一起存储在用户账户表中。

存储密码的步骤:

  1. 使用 CSPRNG 产生足够长的随机盐值。
  2. 将盐值与密码混合,用标准密码哈希函数加密,如Argon2、 bcrypt 、 scrypt 或 PBKDF2 。
  3. 将盐值与相应的哈希值一起存储在用户数据库中。

验证密码的步骤:

  1. 检索用户的盐值和相应的哈希值。
  2. 将盐值混入用户输入的密码,并且使用通用的哈希函数进行加密。
  3. 与上一步的结果相比,它是否与数据库存储的哈希值相同。如果它们相同,则表示密码正确;否则,密码是错误的。

在 Web 在应用程序中,哈希加密将永远在服务端进行

这并不意味着你不应该在浏览器上加密哈希,但如果你这样做了,你必须在服务器上再加密哈希。在浏览器中加密哈希无疑是一个好主意,但在实现时应考虑以下几点:

  • 客户端密码哈希加密不是 HTTPS(SSL/TLS)的替代品。如果浏览器和服务端之间的连接是不安全的,那么中间人攻击可以修改 JavaScript 代码,删除加密函数,从而获取用户的密码。
  • 某些浏览器不支持 JavaScript ,还有一些用户在浏览器中禁用 JavaScript 功能。因此,为了更好的兼容性,您的应用应该检测浏览器是否支持 JavaScript ,如果不支持,就需要在服务端模拟客户端进行哈希加密。
  • 客户端的哈希加密同样需要加盐。显而易见的解决方案是使客户端脚本向服务端请求用户的盐值。但是不提倡这样做,因为它可以让攻击者能够在不知道密码的情况下检测用户名是否有效。既然你已经在服务端上对密码进行了加盐哈希(使用合格的盐值),那么在客户端,将用户名(或邮箱)加上网站特有的字符串(如域名)作为客户端的盐值也是可行的。

使密码更难破解:慢哈希函数( Slow Hash Function)

加盐可以确保攻击者无法使用像查询表和彩虹表攻击那样对大量哈希值进行破解,但依然不能阻止他们使用字典攻击或暴力攻击。高端显卡( GPU )和定制的硬件每秒可以进行十亿次哈希计算,所以这些攻击还是很有效的。为了降低使这些攻击的效率,我们可以使用一个叫做密钥扩展( key stretching)的技术。

这样做的初衷是为了将哈希函数变得非常慢,即使有一块快速的 GPU 或定制的硬件,字典攻击和暴力攻击也会慢得令人失去耐心。终极目标是使哈希函数的速度慢到足以令攻击者放弃,但由此造成的延迟又不至于引起用户的注意。

密钥扩展的实现使用了一种 CPU 密集型哈希函数( CPU-intensive hash function)。要使用标准的算法,比如 PBKDF2 或 bcrypt 。你可以在这里找到 PBKDF2 在 PHP 上的实现。

这类算法采取安全因子或迭代次数作为参数。此值决定哈希函数将会如何缓慢。对于桌面软件或智能手机应用,确定这个参数的最佳方式是在设备上运行很短的性能基准测试,找到使哈希大约花费半秒的值。通过这种方式,程序可以尽可能保证安全而又不影响用户体验。

如果您想在一个 Web 应用使用密钥扩展,须知你需要额外的计算资源来处理大量的身份认证请求,并且密钥扩展也容易让服务端遭受拒绝服务攻击( DoS )。尽管如此,我还是建议使用密钥扩展,只不过要设定较低一些的迭代次数。这个次数需要根据自己服务器的计算能力和预计每秒需要处理的认证请求次数来设置。消除拒绝服务的威胁可以通过要求用户每次登陆时输入验证码( CAPTCHA )来做到。

不可能破解的哈希加密:密钥哈希和密码哈希设备

只要攻击者可以使用哈希来检查密码的猜测是对还是错,那么他们可以进行字典攻击或暴力攻击。下一步是将密钥( secret key)添加到哈希加密,这样只有知道密钥的人才可以验证密码。有两种实现的方式,使用ASE算法对哈希值加密;或者使用密钥哈希算法 HMAC 将密钥包含到哈希字符串中。

实现起来并没那么容易。这个密钥必须在任何情况下,即使系统因为漏洞被攻陷,也不能被攻击者获取。如果攻击者完全进入系统,密钥不管存储在何处,总能被找到。因此,密钥必须密钥必须被存储在外部系统,例如专用于密码验证一个物理上隔离的服务端,或者连接到服务端,例如一个特殊的硬件设备,如 YubiHSM 。

我强烈建议所有大型服务(超过10万用户)使用这种方式。我认为对于任何超过100万用户的服务托管是非常有必要的。

如果您难以负担多个服务端或专用硬件的费用,依然有办法在标准的Web服务端上使用密钥哈希技术。大多数数据库被拖库是由于 SQL 注入攻击,因此,不要给攻击者进入本地文件系统的权限(禁止数据库服务访问本地文件系统,如果有此功能的话)。如果您生成一个随机密钥并将其存储在一个通过 Web 无法访问的文件上,然后进行加盐哈希加密,那么得到的哈希值就不会那么容易被破解了,就算数据库已经遭受注入攻击,也是安全的。不要将密钥硬编码到代码中,应该在安装应用时随机生成。这么做并不像使用一个独立的系统那样安全,因为如果 Web 应用存在 SQL 注入点,那么有可能存在其他一些问题,如本地文件包含漏洞( Local File Inclusion ),攻击者可以利用它读取本地密钥文件。

请注意,密钥哈希并不意味着无需进行加盐。高明的攻击者最终会想方设法找到密钥,因此,对密码哈希仍然需要进行加盐和密钥扩展,这一点非常重要。

其他安全措施

密码哈希仅仅在安全受到破坏时保护密码。它并不能使整个应用更加安全。首先有很多事必须完成,来保证密码哈希值(和其他用户数据)不被窃取。

对您的应用进行第三方“渗透测试”是一个很好的主意。即使最好的程序员也可能会犯错,所以,让安全专家审计代码寻找潜在的漏洞是有意义的。找一个值得信赖的机构(或招聘人员)来定期审计代码。安全审计应该从开发初期就着手进行,并贯穿整个开发过程。

监控您的网站来发现入侵行为也很重要。我建议至少雇用一名全职人员负责监测和处理安全漏洞。如果某个漏洞没被发现,攻击者可能通过网站利用恶意软件感染访问者,因此,检测漏洞并及时处理是极为重要的。

常见疑问

我应该使用什么样的哈希算法?

可以使用:

  • 精心设计的密钥扩展算法如 PBKDF2 、bcrypt 和 scrypt 。
  • OpenWall的的 Portable PHP password hashing framework。
  • PBKDF2在PHP、C#、Java和Ruby的实现。
  • crypt 的安全版本。

不可使用:

  • 快速加密哈希函数,如 MD5 、SHA1、SHA256、SHA512、RipeMD、WHIRLPOOL、SHA3等。
  • crypt()的不安全版本。
  • 任何自己设计的加密算法。只应该使用那些在公开领域中的、由经验丰富的密码学家完整测试过的技术。

尽管目前还没有一种针对MD5或SHA1非常高效的攻击手段,但它们过于古老以至于被广泛认为不足以用来存储密码(可能有些不恰当)。所以我不推荐使用它们。但是也有例外,PBKDF2中经常使用SHA1作为它底层的哈希函数。

当用户忘记密码时如何重置密码?

这是我个人的观点:当下所有广泛使用的密码重置机制都是不安全的。如果你对高安全性有要求,如加密服务,那么就不要让用户重设密码。

大多数网站向那些忘记密码的用户发送电子邮件来进行身份认证。要做到这一点,需要随机生成一个一次性使用的令牌( token ),直接关联到用户的帐号。然后将这个令牌混入一个重置密码的链接中,发送到用户的电子邮箱。当用户点击包含有效令牌的密码重置链接,就提示他们输入新密码。确保令牌只对一个帐号有效,以防攻击者从邮箱获取到令牌后用来重置其他用户的密码。

令牌必须在15分钟内使用,且一旦使用后就立即作废。当用户登录成功时(表明还记得自己的密码), 或者重新请求令牌时,使原令牌失效是一个好做法。如果令牌永不过期,那么它就可以一直用于入侵用户的账号。电子邮件(SMTP)是一个纯文本协议,网络上有很多恶意路由在截取邮件信息。在用户修改密码后,那些包含重置密码链接的邮件在很长时间内缺乏保护,因此,尽早使令牌尽快过期,来降低用户信息暴露给攻击者的风险。

攻击者能够篡改令牌,因此不要把帐号信息和失效时间存储在其中。它们应该以不可猜测的二进制形式存在,并且只用来识别数据库中某条用户的记录。

千万不要通过电子邮件向用户发送新密码。记得在用户重置密码时随机生成一个新的盐值用来加密,不要重复使用已用于密码哈希加密的旧盐值。

如果帐号数据库被泄漏或入侵,应该怎么做?

你的首要任务是,确定系统被暴露到什么程度,然后修复攻击者利用的的漏洞。如果你没有应对入侵的经验,我强烈建议聘请第三方安全公司来做这件事。

捂住一个漏洞并期待没人知道,是不是很省事,又诱人?但是这样做只会让你的处境变得更糟糕,因为你在用户不知情的情况下,将它们的密码和个人信息置于暴露风险之中。就算你还没有完全发生什么事情时,你也应该尽快通知用户。例如在首页放置一个链接,指向对此问题更为详细的说明;如果可能的话通过电子邮件发送通知给每个用户告知目前的情况。

向用户说明他们的密码究竟是如何被保护的:最好是使用了加盐哈希。但是,即使用了加盐哈希,恶意黑客仍然可以使用字典攻击和暴力攻击。如果用户在很多服务使用相同的密码,恶意黑客会利用他们找到的密码去尝试登陆其他网站。告知用户这个风险,建议他们修改所有类似的密码,不论密码用在哪个服务上。强制他们下次登录你的网站时更改密码。大多数用户会尝试“修改”自己的密码为原始密码,以便记忆。您应该使用当前密码哈希值以确保用户无法做到这一点。

就算有加盐哈希的保护,也存在攻击者快速破解其中一些弱口令密码的可能性。为了减少攻击者使用这些密码的机会,应该对这些密码的帐号发送认证电子邮件,直到用户修改了密码。可参考前面提到的问题:当用户忘记密码时如何重置密码?这其中有一些实现电子邮件认证的要点。

另外告诉你的用户,网站存储了哪些个人信息。如果您的数据库包括信用卡号码,您应该通知用户仔细检查近期账单并销掉这张信用卡。

应该使用什么样的密码策略?是否应该使用强密码?

如果您的服务没有严格的安全要求,那么不要对用户进行限制。我建议在用户输入密码时,页面显示出密码强度,由他们自己决定需要多安全的密码。如果你有特殊的安全需求,那就应该实施长度至少为12个字符的密码,并且至少需要两个字母、两个数字和两个符号。

不要过于频繁地强制你的用户更改密码,最多每半年一次,超过这个次数,用户就会感到疲劳。相反,更好的做法是教育用户,当他们感觉密码可能泄露时主动修改,并且提示用户不要把密码告诉任何人。如果这是一个商业环境,鼓励员工利用工作时间熟记并使用他们的密码。

如果攻击者入侵了数据库,他不能直接替换哈希值登陆任意帐号么?

是的,但如果有人入侵您的数据库,他们很可能已经能够访问您的服务端上的所有内容,这样他们就不需要登录到您的帐号,就可以获得他们想要的东西。密码哈希(对网站而言)的目的不是为了保护被入侵的网站,而是在入侵已经发生时保护数据库中的密码。

你可以通过给数据库连接设置两种权限,防止密码哈希在遭遇注入攻击时被篡改。一种权限用于创建用户,一种权限用于用户登陆。“创建用户”的代码应该能够读写用户表;但“用户登陆”的代码应该只能够读取用户表而不能写入。

为什么要使用一种像HMAC的特殊算法,而不是只将密钥混入密码?

如 MD5、SHA1、SHA2 和 Hash 函数使用 Merkle–Damg?rd ,这使得它们很容易受到所谓的长度扩展攻击( length extension attack)。意思是给定的哈希值 H(X),对于任意的字符串 Y,攻击者可以计算出 H(pad(X)+Y) 的值,而无需知道 X 的值。其中, pad(X) 是哈希函数的填充函数。

这意味着,攻击者不知道密钥的情况下,仍然可以根据给定的哈希值 H(key+message) 计算出 H(pad(key+message)+extension) 。如果该哈希值用于身份认证,并依靠其中的密钥来防止攻击者篡改消息,这方法已经行不通。因为攻击者无需知道密钥也能构造出包含 message+extension 的一个有效的哈希值。

目前尚不清楚攻击者如何利用这种攻击来快速破解密码哈希。然而,由于这种攻击的出现,不建议使用普通的哈希函数对密钥进行哈希加密。将来也许某个高明的密码学家有一天发现利用长度扩展攻击的新思路,从而更快的破解密码,所以还是使用 HMAC 为好。

盐值应该加到密码之前还是之后?

无所谓,选择一个并保持风格一致即可,以免出现互操作方面的问题。盐值加到密码之前较为普遍。

为何本文的哈希代码都以固定时间比较哈希值?

使用固定的时间来比较哈希值可以防止攻击者在在线系统使用基于时间差的攻击,以此获取密码的哈希值,然后进行本地破解。

比较两个字节序列(字符串)是否相同的标准做法是,从第一个字节开始,每个字节逐一顺序比较。只要发现某个字节不同,就可以知道它们是不同的,立即返回false。如果遍历整个字符串没有找到不同的字节,可以确认两个字符串就是相同的,可以返回true。这意味着比较两个字符串,如果它们相同的长度不一样,花费的时间不一样。开始部分相同的长度越长,花费的时间也就越长。

例如,字符串 “XYZABC” 和 “abcxyz” 的标准比较,会立即看到,第一个字符是不同的,就不需要检查字符串的其余部分。相反,当字符串 “aaaaaaaaaaB” 和 “aaaaaaaaaaZ” 进行比较时,比较算法就需要遍历最后一位前所有的 “a” ,然后才能知道他们是不同的。

假设攻击者试图入侵一个在线系统,这个系统限制了每秒只能尝试一次用户认证。还假设攻击者已经知道密码哈希所有的参数(盐值、哈希函数的类型等),除了密码的哈希值和密码本身。如果攻击者能精确测量在线系统耗时多久去比较他猜测的密码和真实密码,那么他就能使用时序攻击获取密码的哈希值,然后进行离线破解,从而绕过系统对认证频率的限制。

首先攻击者准备256个字符串,它们的哈希值的第一字节包含了所有可能的情况。他将每个字符串发送给在线系统尝试登陆,并记录系统响应所消耗的时间。耗时最长的字符串就是第一字节相匹配的。攻击者知道第一字节后,并可以用同样的方式继续猜测第二字节、第三字节等等。一旦攻击者获得足够长的哈希值片段,他就可以在自己的机器上来破解,不受在线系统的限制。

在网络上进行这种攻击似乎不可能。然而,有人已经实现了,并已证明是实用的。这就是为什么本文提到的代码,它利用固定时间去比较字符串,而不管有多大的字符串。

“慢比较( slowequals)”函数如何工作?

前一个问题解释了为什么“慢比较”是必要的,现在来解释代码如何工作。

private static boolean slowEquals(byte[] a, byte[] b)
     {
         int diff = a.length ^ b.length;
         for(int i = 0; i < a.length && i < b.length; i++)
             diff |= a[i] ^ b[i];
         return diff == 0;
     }

该代码使用异或运算符“^”来比较两个整数是否相等,而不是“==”运算符。下面解释原因。当且仅当两位相等时,异或的结果将是零。这是因为:

0 XOR 0 = 0,1 XOR 1 = 0,0 XOR 1 = 1,1 XOR 0 = 1

如果我们将其应用到整数中每一位,当且仅当字节两个整数各位都相等,结果才是0。

所以,在代码的第一行中,如果a.length等于b.length ,相同的话得到0,否者得到非零值。然后使用异或比较数组中各字节,并且将结果和diff求或。如果有任何一个字节不相同,diff就会变成非零值。因为或运算没有“置0”的功能,所以循环结束后diff是0的话只有一种可能,那就是循环前两个数组长度相等(a.length == b.length),并且数组中每一个字节都相同(每次异或的结果都非0)。

我们需要使用XOR,而不是“==”运算符比较整数的原因是,“==”通常是编译成一个分支的语句。例如,C语言代码中“ diff &= a == b”可能编译以下x86汇编:

MOV EAX, [A]
CMP [B], EAX
JZ equal
JMP done
equal:
AND [VALID], 1
done:
AND [VALID], 0

其中的分支导致代码运行的时间不固定,决定于两个整数相等的程度和CPU内部的跳转预测机制(branch prediction)。

而C语言代码“diff |= a ^ b”会被编译为下面的样子,它执行的时间和两个变量是否相等无关。

MOV EAX,[A]
XOR EAX,[B]
OR [DIFF],EAX

为何要进行哈希?

用户在你的网站上输入密码,是因为他们相信你能保证密码的安全。如果你的数据库遭到黑客攻击,而用户的密码又不受保护,那么恶意黑客可以利用这些密码尝试登陆其他网站和服务(大多数用户会在所有地方使用相同的密码)。这不仅仅关乎你网站的安全,更关系到用户的安全。你有责任负责用户的安全。

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章