XSRF

跨站请求伪造, 从字面上理解, 指的是站点 A 在用户不知情的情况下, 以用户的身份向向站点 B 发起请求, 对用户在站点 B 的数据进行了更改。

XSRF 的实施, 是基于浏览器工作的一个机制:当向某个域名发起请求时, 浏览器会把该域名下的所有 cookie 发到服务器。

cookie 发到服务器有什么作用?一般而言, cookie 里保存着会话 id, 服务器从cookie 中拿到会话 id,用 id 查找到对应会话对象,如果会话对象里保存的信息指示了用户已经登录,那么服务器就执行请求。

所以,如果用户登录了站点 A, 在站点 A 的 cookie 有效期内, 登录了站点 B, 站点 B 又向站点 A 发起了一个请求(通过什么方式? ajax?还是表单),如“发表新微博, 内容:我是 SB“。浏览器会将请求和站点 A 的 cookie 发送到站点 A, 站点 A 通过cookie 识别到这是一个合法的用户,就按照指示发表了微博。

那么站点 B 如何向站点 A 发起请求呢?过一遍以下候选:

  • ajax

    用 ajax 虽然可以跨域发送请求(可以发送,但是同源限制你不能获取响应的数据),但是它是不能带站点 A cookie 的。

  • form 表单

    form 表单可以无障碍地向站点 A 发送请求并且携带站点 A 的 cookie

  • jsonp,图像 PING

    这两个都无法发送 post 请求。

综上所述, 只要在站点 B 构造一个表单并提交(如何提交?用户点击还是脚本自动?)到站点 A, 就完成了一个 XSRF。

那么怎么预防呢?如果 XSRF 只有构造表单一种方法, 那么我们只要让别的站点无法伪造表单就可以了。

浏览器还有一个工作机制,B域名无法查看 A 域名的 cookie。因此如果站点 A 的表单加上一个隐藏字段,该字段的值取自 cookie 中的某个值,那就实现了 XSRF 的预防。

服务器接到请求后, 查看请求里是否包含了隐藏字段, 并且隐藏字段的值和 cookie 的某个值是否一致,就可以判断出该请求是否是一个伪造的请求。由于站点 B 无法构造一个正确的隐藏字段的值,也就没法发起 XSRF。

最后要考虑的就是隐藏字段采用什么值了,总的原则就是一个不容易猜到的,没有规律的值。方法通常是对 sessionID + 时间戳 + 服务器设定的密语进行 hash 得到的值。