攻击者会在用户不知情的情况下通过用户浏览器向网站后端发起请求。攻击者可以通过XSS攻击的方式触发CSRF攻击。
比如,在一个未做好安全防范的聊天室或者论坛上,攻击者发送了一个img标签:
<!-- 注意:这个img标签的src属性值并非一个真正的图片地址,而是一个请求银行网站的链接地址 -->
<img src="https://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory" />
<!-- 注意:这个img标签的src属性值并非一个真正的图片地址,而是一个请求银行网站的链接地址 -->
<img src="https://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory" />
如果一个用户在访问这个渲染好的html片段之前正好访问过这个银行网站, 并且cookie信息未过期,且银行方也没有做除了cookie之外的其他校验,那么这个用户的钱就有可能被直接转走了!
通过img标签触发的都是get请求,那是不是转账这种敏感请求都用post不用get就没这个问题了呢?也不是的,因为攻击者也可以通过构造form表单或者直接注入js脚本来实现非get请求。
<form action="https://bank.example.com/withdraw" method="POST">
<input type="hidden" name="account" value="bob" />
<input type="hidden" name="amount" value="1000000" />
<input type="hidden" name="for" value="mallory" />
</form>
<script>
window.addEventListener('DOMContentLoaded', () => {
document.querySelector('form').submit();
})
</script>
<form action="https://bank.example.com/withdraw" method="POST">
<input type="hidden" name="account" value="bob" />
<input type="hidden" name="amount" value="1000000" />
<input type="hidden" name="for" value="mallory" />
</form>
<script>
window.addEventListener('DOMContentLoaded', () => {
document.querySelector('form').submit();
})
</script>
上面这段代码,只要是放在一个看不叫的iframe标签内部的话,触发时就不会导致页面跳转,也就不会被用户感知到。
预防措施:
跨站脚本攻击(XSS)是攻击者通过向网站的客户端注入恶意代码来实现的。 受害者执行这段代码之后,攻击者就可以绕过访问权限的拦截,模拟用户行为。
如果Web应用没有采用足够的校验和编码措施来进行预防的话,这些攻击就可能会得逞。 用户的浏览器无法判断这些恶意脚本是“恶意的”,所以会允许它们访问cookie、token或者其他网站敏感信息,也会允许这些代码去修改HTML内容。
当动态内容,或者来自不可信来源(通常是网络请求)的数据,在未经校验是否有恶意内容的情况下被发送给用户时,就容易出现跨站脚本攻击。
这些恶意内容通常包括JavaScript代码,有时候也包括HTML、Flash,或者其他浏览器可以执行的代码。XSS攻击有很多类型,但通常包括:
我们可以将跨站脚本攻击分成3类:
前端加密的意义不是为了防止中间人,而是提供一种隐私保护服务。
这样即使因为使用的是http协议导致通信过程被攻击者拦截, 攻击者直接拿到的也不是用户的原始密码,而是加密字符串,这个加密字符串可以直接被用于当前网站。 但是当攻击者拿这个加密字符串去其他网站尝试使用时,只要其他网站使用的不是同一套加密逻辑,就没有用。 就是说用户在其他网站使用这个密码还是相对安全的。
攻击者如果能拿到密码明文的话,还是很危险的,前端加密一定程度上可以增加这个难度(增加了攻击者从加密字符串破解出明文密码的过程)。
一般做法是使用https协议,并且对密码采用非对称加密算法(如RSA)处理后再进行传输。
使用https协议,可以避免用户密码在网络上裸奔。http协议是明文传输的,有3大风险:
https原理是什么呢?为什么它能解决http的三大风险呢?
https = http + SSL/TLS, SSL/TLS 是传输层加密协议, 它提供内容加密、身份认证、数据完整性校验, 以解决数据传输的安全性问题。
一次完整的https请求流程如下:
https的数据传输过程,数据都是密文的。 但是,即时使用了https协议传输密码信息,也不一定就安全了。 比如,https完全就是建立在证书可信的基础上的。 如果遇到中间人伪造证书,一旦客户端通过验证,安全性就没了。 通过伪造证书,https也是可能被抓包的。
如上所述,即使用了https协议传输用户密码,只要用户信任了伪造证书,也还是会有安全隐患的。所以,对于密码,传输前还是需要先进行加密的。
加密算法有对称加密和非对称加密两种。
加密和解密使用“相同密钥”的加密算法。 使用对称加密算法时,需要考虑如何将密钥给到客户端的问题,如果还是通过网络传输的方式,如果密钥在传输过程中被中间人拿到的话,还是有风险的。
常见的对称加密算法有:
非对称加密算法需要2个密钥(公钥和私钥)。公钥和私钥是成对存在的,用公钥对数据进行加密,用对应的私钥才能解密。 使用费对称加密算法时,也需要考虑如何将密钥、公钥给到客户端的问题, 如果公钥在网络传输过程中被中间人拿到的话,中间人可以伪造公钥,把伪造的公钥给客户端,然后用自己的私钥解密从客户端过来的加密数据。
常见的非对称加密算法有:
密码安全送达服务端后,一定不能明文存储密码到数据库。可以先用哈希摘要算法加密密码,然后再保存到数据库。
哈希摘要算法:只能从明文生成一个对应的哈希值,不能反过来根据哈希值得到对应的明文。
MD5是一种非常经典的哈希摘要算法,被广泛应用于数据完整性校验、数据摘要、数据加密等。
直接MD5加密
对原始密码直接进行MD5加密的话是很不安全的。因为攻击者用彩虹表可以很容易破解出密码。 如果把所有20位以内的数字和字母组合的密码全部计算其MD5哈希值,并把密码和对应哈希值存到一个超大数据库里,就是一个彩虹表了。
提醒:网络上已经有很多MD5免费破解网站了,可以自己随便试。
优化方案:密码加盐后再进行MD5加密
先对字段进行加盐处理,再进行MD5加密,即MD5(password + salt)。只要salt够长,是没有办法通过彩虹表反查的。
在密码学中,通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这个过程称为“加盐”。
加盐有几个注意事项
即使MD5加密前加了盐,密码仍有可能被暴力破解。可以采取更慢一点的算法,增加攻击者破解密码所需的成本,迫使攻击者放弃攻击。
为了应对暴力破解,我们需要非常耗时而非高效的哈希算法。 BCrypt算法的特点是可以通过参数设置重复计算的次数,重复计算的次数越多耗时越长。 如果计算一个哈希值需要耗时1秒以上,破解一个6位纯数字密码就需要耗时11.5天以上,更不要说高安全级别的密码了。暴力破解密码的可能性就很低了。
实际上,Spring Security 已经废弃了MessageDigestPasswordEncoder,推荐使用BCryptPasswordEncoder,也就是BCrypt来进行密码哈希。
感知到暴力破解危害时,应开启短信验证、图形验证码、账号暂时锁定等防御机制来进行抵御。
如何明确是暴力破解的话,可以采取封IP等措施。