写在前面:加密和签名是两回事,加密的目的是防止信息泄露,签名的目的是防止篡改和伪造
MD5、SHA-1、SHA-256、HMAC-SHA256等属于哈希算法,计算数字摘要,不可逆,有碰撞
DES、AES、RSA等属于加密算法,对数据进行加解密,可逆
MD5签名
MD5签名通常先按照一定规则排列待签名数据,进行加盐(拼key),然后使用MD5摘要算法计算摘要,得到的散列值即为sign
MD5验签即按照相同的规则排列待签名数据,使用相同的key,然后使用MD5摘要算法计算摘要,对比自己得到的sign值和对方传过来的sign值是否相等
参考资料:MD5算法原理
MD5签名的优点
- 运行速度快
- 任意长度的数据,算出的MD5值长度都是固定的(128bit,即32位16进制数)
MD5签名的缺点
MD5作为一种散列算法,虽然很难发生散列碰撞,但是仍然存在两种不同数据会发生碰撞
MD5碰撞
已知明文A,可以计算得到另一个明文B和A的MD5值相同,但是并不能保证B是一段有意义的文字
未知明文A,已知MD5值X,无法计算得到明文A
MD5破解方法(MD5值反推明文)
暴力枚举、字典法、彩虹表法(对字典表的优化)
参考资料:MD5破解方法
MD5算法已不那么不安全
- MD5不应该被用于软件/文件完整性的校验
- MD5不应该被用于数字证书
- 禁止将密码等敏感信息直接MD5后存储,必须加盐
- MD5作为接口参数的签名算法,拼的key必须严格保密,所以不适合做提供给外公司调用的接口的签名算法
- MD5作为接口参数的签名算法,key一般拼接在待签名数据的后面,可能会被暴力枚举法攻击得到key,所以key不能太简单
什么情况下适合用MD5作为签名算法?
- 提供给公司内部调用,但是又有一定安全性要求的接口。比如阅饼赠送接口、支付宝单笔转账接口等,有wiki查询权限的人就可以手工调用
- 对性能要求比较高,又有一定安全性要求的接口
SHA-1算法
网上的这篇文章说的比较清楚:SHA1算法原理
SHA-1算法已不那么不安全
SHA-1一般被应用于数字证书的签名哈希算法,或者RSA签名中的哈希算法
经过权威机构证实,sha1算法的不安全性越来越高,sha指纹造假成本越来越低,随即微软、谷歌等IT巨头相继发布弃用sha1哈希算法声明,第三方认证机构自2016年1月1日起,将全面停止签发SHA1算法的数字证书。
SHA-256算法
网上的这篇文章说的比较清楚:SHA256算法原理
小tips
SHA-1算法和SHA-256算法并不是近亲,SHA-256算法属于SHA-2算法。SHA-1是160位的哈希值,而SHA-2是组合值,有不同的位数,SHA-256就是256位的SHA-2。SHA-1算法和MD5算法都是由MD4算法导出,所以这俩是近亲。
SHA-256算法的应用
- 数字证书的签名算法,替代SHA-1
- 比特币,区块链
- RSA签名中使用的哈希算法
MD5、SHA-1、SHA-256比较
- 速度:MD5>SHA-1>SHA-256
- 安全性:MD5<SHA-1<<SHA-256
- 哈希值长度:MD5(128bit)<SHA-1(160bit)<SHA-256(256bit)
什么情况下适合用SHA-256作为签名算法?
- SHA-256做签名仍然需要加盐(拼key),所以适合内部使用
- 时间消耗大约是MD5的1.5倍到2倍,CPU负载也更高,在性能要求不高的情况下代替MD5
慢哈希bcrypt
顾名思义,慢哈希就是很慢,主要为了防止暴力破解。由于慢,通常都用在客户端或者对性能没什么要求的场景
加盐
随机盐还是固定盐?
- 固定盐就是所有的密码使用统一的salt,放在代码里,salt需要有一定复杂度
- 随机盐可以使用用户的一个唯一的、必有的、不可更改的属性来生成盐,需要同样存到库里,但是可以分开存储
固定盐的缺点
- 会被黑客找到弱密码:统计哪些哈希值出现的频率较高,哪些就是弱密码
- 有可能被彩虹表破解
加盐的方式
业内通用的一般是MD5(MD5(password)+salt)或SHA256(SHA256(password)+salt)
服务端加盐
客户端进行SHA256(password),并传输给服务端,服务端进行SHA256(SHA256(password)+salt)
- 更安全的方式是同时使用https
- 更更安全的方式可以参考扩展阅读中的“即使被拖库,也可以保证密码不泄露”
“弱密码”究竟安不安全?为什么现在的支付密码普遍是六位数字?
- 复杂的密码用户容易忘记
- 首先要登录才允许支付
- 其他安全手段,比如:绑定手机号,绑定设备,在设备更换时发送验证码
- 有限的允许错误次数
扩展阅读
RSA签名
RSA签名过程
- 按照一定规则排列待签名数据得到待签名字符串
- 使用MD5、SHA-1、SHA-256(推荐)等哈希算法计算出待签名字符串的摘要
- 对摘要使用私钥加密
RSA验签过程
- 对签名进行公钥解密,得到摘要
- 按照一定规则排列待签名数据得到待签名字符串
- 使用约定的哈希算法计算出待签名字符串的摘要
- 比较两个摘要是否相同
RSA签名的优点
- 安全性高
- 使用非对称加密,有助于对密钥的保护
RSA签名的缺点
慢
什么情况下适合用RSA签名算法?
- 性能要求不高但安全性要求高
- 给其他公司的接口
- 服务端和客户端之间的接口
ps:小数据如果既要加密又要签名,可以直接使用RSA私钥加密整个数据,接收方公钥解密,不做签名
拼接待签名字符串的一些问题
- 当url中和body中有一个相同的参数但是其值不同,验签和实际使用的参数可能不一致,产生漏洞
- 约定只使用get或post的参数
- 验签放在取值之后,用实际取到的参数拼接待签名字符串
- k,v不放到字典中,而是先拼接成k=v,然后放到set中
- 若存在两个接口,参数相同,签名key也相同,可以直接拿A接口的参数去请求B接口,产生漏洞
- 定一个比如叫service_name的参数,来约定接口名称,参与签名
- 将接口url以一定的规则参与到签名中
- 指定哪些参数需要参与签名
- 不灵活,比如接口请求增加需要参与签名的参数要改动签名的逻辑,返回增加需要参与签名的参数需要通知所有调用方
- 指定哪些参数不需要参与签名
- 通常不需要参与签名的参数比较固定,例如sign_type
- 使用MD5作为签名算法时,可能会被杂凑攻击
签名无法防止重放攻击
比如赠送代金券的接口,拿到一个真实请求以后重复调用。
解决方案:
- 增加时间戳字段(需参与签名),保存X时间的请求验证是否重复,时间戳超过X时间则认为请求无效
- 由业务来保证,增加一个唯一的流水号,不重复赠送
算法速度比较
环境:本地 window10 i5-6300U
语言:java
文本:汉字,length770
字符集:utf-8
-
循环计算10000次,一共执行10次,取平均毫秒数
- md5:128
- sha1:158
- sha256:218
-
循环计算1000次,一共执行10次,取平均毫秒数
- rsa-1024-sha1:879
- rsa-1024-sha256:987
- rsa-2048-sha256:4309