这是我在知乎的一个回答。原提问是如何在单页应用下进行 XSRF 防护。
XSRF(CSRF) 攻击的原理是什么?就是攻击者能猜测出所有的需要提交的内容以及类型,所以所有的解决方案共同出发点就是加一个攻击者也不知道随机值发送给后端验证就可以防范。
有很多解决方案,COOKIE-session,很不友好的所有表单都得填写验证码,还有一种很少人知道 JSON Web Token。
验证码(图形或者手机)这种就不说了吧,这个在互联网场景中因为用户体验原因几乎没有应用的。
首先要知道直接让后端验证 COOKIE 是否存在正确是不可取的,因为所有请求都会自动附带请求所在域的 COOKIE,当然只验证 http referer 也是不靠谱的。
正确的方式是当用户进行登录请求的时候,这时候后端应该把包含 xsrf 字段的 COOKIE 保存在 session 中并且返还给前端,前端需要获取到 COOKIE 中的值并且能放入 ajax 请求体或请求头中,后端把这个值与 session 中的相应值进行判断就可以了,根据跨域不可访问不同域的 COOKIE ,攻击者也很难猜测出 xsrf 的值,那么这样就防范了 xsrf 攻击。
所以这里对 xsrf COOKIE 不能设置 httpOnly(当然就会有 XSS 问题,后面会提),同时提一句所以的 Token 必须得让后端设置 expire 过期时间。
这个 axios 就提供了这个功能,只要设置约定好 xsrf COOKIE字段名就可以了,axios 获取到值后默认是放入 request header 中,这也是业界最流行的方式。
如果不是单页应用都是后端在表单中加入一个隐藏的表单域。
<input type&#61;"hidden" name&#61;"_token" value&#61;"lAfHB..">
当然还有JWT&#xff0c;这个主要应用场景是 app&#xff0c;因为 app 通常没有 COOKIE&#xff0c;当然也有应用到 Web 中的&#xff0c;要讲这个就有点多了&#xff0c;和上述也差不多。
简单说&#xff0c;JWT 就是服务端和客户端约定好一个Token格式&#xff0c;最后用密钥进行签名 base64 编码后放入请求头即可&#xff0c;客户端存放这个签名的内容通常会放在 localstorage 中&#xff0c;也有放在 COOKIE 中的。JWT应用了哈希签名的密码学技术&#xff0c;相比 COOKIE-session 的方式就是服务端可以不用&#xff08;在内存或者缓存&#xff09;存放 session&#xff0c;能节省存储资源&#xff0c;不过同时服务器需要通过计算来验证也浪费了计算资源。详细的说明可以参考&#xff1a;讲真&#xff0c;别再使用JWT了&#xff01;
现有的产品为了更安全还需要考虑 XSS 攻击&#xff0c;这个就是有些恶意脚本或者插件不存在跨域问题&#xff0c;所以能获取到 COOKIE 和 localstorage 的值。
很安全的方式就是把 XSRF Token 加入到 JWT 中&#xff0c;并且把 JWT 存放在设置 httpOnly 的 COOKIE 中&#xff0c;然后单独把 XSRF Token (一般而言就是一个随机值)设置在 httpOnly&#61;false 的 COOKIE 中&#xff0c;前端请求时&#xff0c;需要获取 XSRF Token 并放入请求头&#xff08;RequestHeader&#xff09;。服务器端可以直接验证JWT中XSRF的值和XSRF的值即可。因为用了哈希密钥签名的技术&#xff0c;这样就可以防止篡改内容。
这样的安全防护就能抵御所有的 XSRF 攻击了。