CSRF(Cross-site request forgery)跨站请求伪造,是一种常见的攻击方式。是指 A 网站正常登陆后,cookie 正常保存,其他网站 B 通过某种方式调用 A 网站接口进行操作,A 的接口在请求时会自动带上 cookie。
上面说了,SOP 可以通过 html tag 加载资源,而且 SOP 不阻止接口请求而是拦截请求结果,CSRF 恰恰占了这两个便宜。
所以 SOP 不能作为防范 CSRF 的方法。
对于 GET 请求,直接放到<img>
就能神不知鬼不觉地请求跨域接口。
对于 POST 请求,很多例子都使用 form 提交:
<form action="<nowiki>http://bank.com/transfer.do</nowiki>" method="POST"> <input type="hidden" name="acct" value="MARIA" /> <input type="hidden" name="amount" value="100000" /> <input type="submit" value="View my pictures" /> </form>
归根到底,这两个方法不报跨域是因为请求由 html 控制,你无法用 js 直接操作获得的结果。
对于 ajax 请求,在获得数据之后你能肆意进行 js 操作。这时候虽然同源策略会阻止响应,但依然会发出请求。因为执行响应拦截的是浏览器而不是后端程序。事实上你的请求已经发到服务器并返回了结果,但是迫于安全策略,浏览器不允许你继续进行 js 操作,所以报出你熟悉的 blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
。
所以再强调一次,同源策略不能作为防范 CSRF 的方法。
不过可以防范 CSRF 的例外还是有的,浏览器并不是让所有请求都发送成功,上述情况仅限于简单请求,相关知识会在下面 CORS 一节详细解释。
SOP 被 CSRF 占了便宜,那真的是一无是处吗?
不是!是否记得 SOP 限制了 cookie 的命名区域,虽然请求会自动带上 cookies,但是攻击者无论如何还是无法获取 cookie 的内容本身。
所以应对 CSRF 有这样的思路:同时把一个 token 写到 cookie 里,在发起请求时再通过 query、body 或者 header 带上这个 token。请求到达服务器,核对这个 token,如果正确,那一定是能看到 cookie 的本域发送的请求,CSRF 则做不到这一点。(这个方法用于前后端分离,后端渲染则可以直接写入到 dom 中)
示例代码如下:
var csrftoken = Cookies.get('csrfToken') function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return /^(GET|HEAD|OPTIONS|TRACE)$/.test(method) } $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader('x-csrf-token', csrftoken) } }, })