2011年12月,CSDN的安全系统遭到黑客攻击,600万用户的登录名、密码及邮箱遭到泄漏。随后,CSDN"密码外泄门"持续发酵,天涯、世纪佳缘等网站相继被曝用户数据遭泄密。泄密就算了,更让人无语的是密码等信息都是明文存储,导致黑客直接拿到了信息而无需破解,这一系列事件发生后,密文存储用户信息的方式开始流行。
这篇博客会介绍一些经典的加密算法,其中md5、sha1、sha256等加密算法虽然是单向散列算法,是不可逆的,但是由于其太经典了,因此很可能被暴力破解,就是他们会把常见字符进行组合使用这些经典加密算法后存到数据库中,需要解密时就把需要解密的字符串与数据库中的进行比对,因此都有被破解的风险,因此这类算法并不适合用在密码保护场景。
为什么需要把应用程序中用户的密码进行散列化
当设计一个需要接受用户密码的应用时, 对密码进行散列是最基本的,也是必需的安全考虑。 如果不对密码进行散列处理,那么一旦应用的数据库受到攻击, 那么用户的密码将被窃取。 同时,窃取者也可以使用用户账号和密码去尝试其他的应用, 如果用户没有为每个应用单独设置密码,那么将面临风险。
通过对密码进行散列处理,然后再保存到数据库中, 这样就使得攻击者无法直接获取原始密码, 同时还可以保证你的应用可以对原始密码进行相同的散列处理, 然后比对散列结果。
需要着重提醒的是,密码散列只能保护密码 不会被从数据库中直接窃取, 但是无法保证注入到应用中的 恶意代码拦截到原始密码。
为何诸如 md5() 和 sha1() 这样的常见散列函数不适合用在密码保护场景
MD5,SHA1 以及 SHA256 这样的散列算法是面向快速、高效 进行散列处理而设计的。随着技术进步和计算机硬件的提升, 破解者可以使用“暴力”方式来寻找散列码 所对应的原始数据。
因为现代化计算机可以快速的“反转”上述散列算法的散列值, 所以很多安全专家都强烈建议 不要在密码散列中使用这些散列算法。
如果不建议使用常用散列函数保护密码, 那么我应该如何对密码进行散列处理
我们可以在进行散列处理的过程中加入的一些数据,用来避免从已计算的散列值表 (被称作“彩虹表”)中 对比输出数据从而获取明文密码的风险。
简单而言,“盐”就是为了提高散列值被破解的难度而加入的少量数据。 现在有很多在线服务都能够提供计算后的散列值以及其对应的原始输入的清单, 并且数据量极其庞大。 通过加“盐”就可以避免直接从清单中查找到对应明文的风险。
如果不提供“盐”,password_hash() 函数会随机生成“盐”。 非常简单,行之有效。
虽然这些算法都有可能被破解,但是这篇博客还是会介绍几种推荐保存的密码的算法的。
是单项散列加密算法,不可逆。MD5加密是php最常见的不可逆加密算法。来自 RFC 1321 的解释 - MD5 报文摘要算法:MD5 报文摘要算法将任意长度的信息作为输入值,并将其换算成一个 128 位长度的"指纹信息"或"报文摘要"值来代表这个输入值,并以换算后的值作为结果。MD5 算法主要是为数字签名应用程序而设计的;在这个数字签名应用程序中,较大的文件将在加密(这里的加密过程是通过在一个密码系统下[如:RSA]的公开密钥下设置私有密钥而完成的)之前以一种安全的方式进行压缩。
语法如下:
string md5 ( string $str [, bool $raw_output = false ] )
参数一的$str
是原始字符串
参数二的$raw_output
是可选值,默认为false,如果设置为true,则md5报文摘要将以16字节长度的原始二进制格式返回。返回以32位字符十六进制数字形式返回散列值。
示例如下:
$str = "eyes";
echo md5($str);
echo "
";
echo md5($str, true);
sha1加密算法和MD5加密算法一样时不可逆的,有两个参数,一个是要加密的字符串,第二个是bool值,如果指定第二个参数为TRUE,则返回二进制格式的字符串,如果不指定则默认为FALSE,返回的是40位的16进制格式的字符串
语法如下:
string sha1(string $str[, bool $raw_output=false])
参数一的$str
是加密的字符串
参数二的$raw_output
:如果被设置为TRUE,那么sha1摘要将以20字符长度的原始格式返回,否则返回值是一个40字符长度的十六进制数字
crypt()加密算法是一种不可逆的加密算法,他有两个参数,一个是需要加密的字符串,另外一个是盐值(或者成为干扰字符串)。这个函数在不同的操作系统上的表现形式也是不一样的,会自动检测。该算法可以在PHP5.3及后续版本使用, 它支持多种散列算法。 针对每种受支持的散列算法,PHP 都提供了对应的原生实现, 所以在使用此函数的时候, 你需要保证所选的散列算法是你的系统所能够支持的。
如果使用 crypt() 函数来进行密码验证, 那么你需要选择一种耗时恒定的字符串比较算法来避免时序攻击。就是说,字符串比较所消耗的时间恒定, 不随输入数据的多少变化而变化,PHP 中的 ==和=== 操作符 和 strcmp() 函数都不是耗时恒定的字符串比较, 但是 password_verify() 可以帮你完成这项工作。 我们鼓励你尽可能的使用原生密码散列API。
语法如下:
string crypt(string $str[, string $salt])
返回一个基于UNIX DES算法或系统上其他可用的替代算法的散列字符串。
参数一的str
: 需要加密的明文。
参数二的$salt
:加密时的干扰串,使编码更安全,一般是两个字符
然而,如果没有salt的话,如果加密时没有加上这个$salt
参数,则会随机生成一个干扰串,且crypt()创建出来的会是弱密码。 php 5.6及之后的版本会在没有它的情况下抛出一个 E_NOTICE 级别的错误。为了更好的安全性,所以需要确保指定一个足够强度的盐值。
$str = "eyes++";
echo crypt($str);
echo "
";
echo crypt($str, "eyes");
图片中以$1开头,$结尾,中间不超过12个字符,是MD5的特色,这个在下面会提到。
在 crypt() 函数支持多重散列的系统上,下面的常量根据相应的类型是否可用被设置为 0 或 1:
$str = "eyes++";
echo crypt($str);
echo "
";
echo crypt($str, "eyes");
echo "
";
if (CRYPT_STD_DES) {
echo "标准DES算法: " . crypt($str, "eyes");
echo "
";
echo "标准DES算法: " . crypt($str, "eyes");
}
echo "
";
if (CRYPT_MD5) {
echo "MD5 散列: " . crypt($str, '$1$abcd$');
echo "
";
echo "MD5 散列: " . crypt($str, '$1$abcd$');
}
echo "
";
if (CRYPT_SHA256) {
echo "SHA-256 算法: " . crypt($str, '$6$1sas515');
echo "
";
echo "SHA-256 算法: " . crypt($str, '$6$1sas515');
}
echo "
";
if (CRYPT_SHA512) {
echo "SHA-512 算法: " . crypt($str, '$6$1sa151acacsas515');
echo "
";
echo "SHA-512 算法: " . crypt($str, '$6$1sa151acacsas515');
}
如果还想了解更多相关内容,欢迎来我的个人网站看看:eyes++的个人空间