跳至主要內容

CSRF介绍

PPLong大约 5 分钟学习WebWeb Security

CSRF介绍

看了CORS和SOP后还想继续了解一下Web安全的一些攻防,怕以后搭建后端的时候可能会有坑,这里先入个门

介绍

Cross-Site Request Forgery 跨站请求伪造,也可称为“One Click Attack” 或者 “Session Riding”。通过伪装网站受信任的用户发送的请求来破坏数据

引入

为什么会发生CSRF攻击? ——浏览器的隐式认证

一般网站不会直接通过无Cookie的GET请求来直接修改内部数据,这也是不推荐的做法。一般需要在网站用户登录后,在本地浏览器和服务器端分别存有有效Cookie和Session的情况下,请求携带Cookie才能通过认证,进而修改数据。
而在本地含有某域的Cookie的情况下,对该域的请求浏览器会自动携带Cookie(而Cookie具有不可跨域性,所以在不同域下的请求不会携带不同的Cookie)

考虑一个场景:

  • 我在网站A成功登录了自己的用户,未注销
  • 在Cookie的有效期内,我在同一浏览器的另一个Tab页面内访问了另一个恶意网站
  • 假设这个恶意网站通过script或者img的src发送POST或者GET请求,指向了网站A 更改用户数据的操作(因为浏览器隐式认证的功能,在Cookie存在时访问网站会自动携带上网站的Cookie)[从而使得网站A误以为是用户进行的操作]

由于携带了 Cookie,且发送了跨域的请求,服务器Access-Control-Allow-Origin没有设置为* ,那么一定会触发SOP,那么:

SOP能限制CRSF攻击吗?

不能,因为SOP只是拦截了服务器返回的相应,但发出的请求仍然是成功处理的(可以看响应码返回的是200),可以这样理解:SOP是对浏览器用户做了保护,但并没有为请求的对象做保护。(这里自己想了很久,以为SOP直接拦截了客户端发出的请求,后来实验才发现只是拦截了服务器返回的响应)

实验

搭建一个简易的后台环境模拟服务器,通过存取Cookie来观察是否存在浏览器的隐式认证

Utils : Tornado Chrome

思路:

  • 通过直接浏览器访问服务器一URL,处理GET请求,并在服务端为response塞入一个自定义Cookie
  • 不清除该Cookie ,访问恶意网站,其中恶意网站的Script包含一个发送到服务器的POST请求
  • 查看该请求是否携带前者的Cookie
class MainHandler(tornado.web.RequestHandler):
    # 处理GET请求, 添加Cookie
    def get(self):
        self.set_cookie('zyl', 'zzzzz')
模拟登录
模拟登录
<script>
    // 服务端发送POST请求
    var xhr  = new XMLHttpRequest();
    xhr.open("POST","http://localhost:9002/post", true)
    var postData = {
    'name':'value'
    }
    // 重要, 使得发送的请求能够附带Cookie参数
    xhr.withCredentials = true
    xhr.send(postData)
</script>

可以看到,由恶意页面发送的请求确实携带了目标网站的Cookie,这里需要区分一点的是:恶意网站本身并不知道服务器保存的正常网站的Cookie是什么,它只是利用了隐式认证的特点,通过浏览器发送了这个带有Cookie的请求。而服务端恰好又认为,既然你带了Cookie,那你肯定是受信任的,那我就允许这个操作,结果就会引发悲剧....

恶意页面中向服务端发送的POST请求
恶意页面中向服务端发送的POST请求

一个超级超级超级小的坑

怪自己对HTTP请求各种参数和XHR配置不熟悉,这里其实最开始使用的是img src的请求嵌入没有观察到浏览器有附带Cookie,然后使用XHR发送POST请求也没有观察到,其实是因为XHR没有配置withCredentials = true,导致请求本身没有携带Cookie,卡了很久......(因为自己一直在想,我服务端是不是出了什么问题才导致不能请求不携带Cookie?但其实服务端本身没有错,问题是在恶意网站,因为是恶意网站发出的请求,所以恶意网站会想尽办法让浏览器带上Cookie)

XHR的发送请求的Cookie携带问题解决了,但通过img的src请求还是不能验证带有Cookie,这里还存有疑惑❓

如何防范

Referrer验证

在请求体中加入Referrer的验证,判断是否是从指定的网站发送的请求,不过Referrer作为请求头可以更改,所以只能过滤一些低级的攻击

验证码判断

很多的网站在进行关键操作时都需要进行验证码判断,就是为了确保你做的操作一定是人工进行的而非机器请求,一是为了防止你过度请求,减少数据库压力,二是确保这个请求确实是你发出的(例如删除个人信息等),而非恶意网站发出

Anti CSRF Token(常用)

在许多请求体中,可能都会见到csrf-token类似这样的请求头。服务端在生成页面或者请求时会给一个Token,(在页面中放在Head处),并且在每次重新时都不一样,发送请求时会发送这个Token,在服务器过滤器中会验证这个Token,验证成功后就会销毁掉

由于网页发送请求时会去找head meta中的csrf-token并装进请求头中,恶意网站去伪造请求时却获取不到这个值(无法进入页面去取head值,也不能预测到token的值),所以就能够成功的进行防御

未完........