造成 CSRF 漏洞的根本原因是服务端使用 cookie 数据,直接或间接的作为验证用户身份的依据。因为浏览器不管是在哪个站点下点击链接,都会自动的把该链接对应域下的 cookie 添加到请求头上。
比如最简单的认证实现方式是登录时把用户信息记录到 session 中,之后服务端根据 cookie 中的 sessionid 类似的字段查找对应的 session 去获取用户信息。该实现问题出在服务端无法根据请求头数据判定该操作是用户意愿,还是他人恶意添加诱导用户点击的链接。
在使用 session 记录用户登录信息的情况下防止 CSRF 攻击
- 可以在登录认证成功后,生成随机的字符串作为 CSRF 令牌,并添加到 session 和 cookie 中。
- 浏览器端判断响应头上有 CSRF 令牌,则把该 CSRF 令牌添加到 localStorage 中和从 cookie 中移除该 CSRF 令牌。
- 之后浏览器在发送给服务端的请求头带上 CSRF 令牌。
- 服务端比对请求头上 CSRF 令牌是否和 session 中的 CSRF 令牌是否一致,作为判断是否 CSRF 攻击的依据。
不使用 session 记录用户登录信息的情况下防止 CSRF 攻击
比如浏览器端把服务端返回的访问令牌存放到 localStorage 中,然后在向服务端发请求时添加该访问令牌,那么不满足被 CSRF 攻击的条件。
不使用 session 但支持记住密码功能的情况下防止 CSRF 攻击
虽然 CSRF 攻击造成的请求不会带上访问令牌,但会把保存在 cookie 中的 remember-me 自动添加到请求头上,服务端将会根据 remember-me 自动登录,也就满足被 CSRF 攻击的条件。此种情况解决的方式跟使用 session 记录用户登录信息的情况下相似,通过添加到浏览器 localStorage 的 CSRF 令牌解决,不同的是服务端要在不使用 session 的情况下验证请求头上的 CSRF 令牌。