web安全漏洞之CSRF

一.CSRF(跨站请求攻击)攻击的原理:

1.攻击原理:

  1. 网站使用cookie来存放用户的登录凭证
  2. cookie会在同源的http请求中自动携带

2.基本攻击过程:

  1. 用户登录了受信任网站A,并在本地生成网站A的cookie
  2. 不登出网站A(A网站的cookie还有效),访问了危险网站B
  3. 危险网站B里面隐藏了一些访问A的接口,用户在访问B网站的时候,就会不自觉的去调用访问A网站的接口,这时候因为A网站的cookie还有效,所以危险网站B里面隐藏的对A的接口就能访问成功

二.CSRF攻击示例:

1.示例:

  • 例如,aaa.com这个网页都是通过session_id来记录用户的登录状态的
  • aaa.com页面上有一个对作品点赞的功能,点赞提交地址为aaa.com/api.like?id=777
  • 小明已经登录了ww.aaa.com,自然aaa.com这个网站就会将小明的登录状态session_id存在cookie
  • 黑客自己创建了另外一个网站bbb.com,并在页面中放了这样一个元素<img src="aaa.com/api.like?id=888">,这样的话,一旦用户进入这个bbb.com页面,就会请求aaa.com这个网站的点赞接口aaa.com/api.like?id=888,而且点赞的用户对象是888
  • 小明登录了aaa.com,且表明身份信息的cookie还没有失效
  • 最后因为小明的登录信息尚未过期,访问时就会带上aaa.com的cookie(该过程详见 这里),那就等于给id为888这个作品点赞了

2.常见的CSRF攻击手段总结:

  1. 欺骗受害用户完成该用户权限许可的任意操作,例如:
    • 获取用户的隐私数据——诱骗用户调用获取隐私数据的接口
    • 更改用户账号的内容——诱骗用户调用修改账号信息的接口
    • 购物消费——诱骗用户调用消费接口
    • ...
  2. 配合其他漏洞攻击
  3. CSRF蠕虫——即产生蠕虫效果,使CSRF攻击一传十,十传百
    • 示例:有一个聊天网站A,它的 ++获取好友列表接口++ 和 ++私信好友接口++ 都存在CSRF漏洞,攻击者可以将其组合成一个CSRF蠕虫:
      • 用户已经登录了网站A,且不登出
      • 黑客发布一个危险网站B,用户访问时,就会被诱骗访问获取好友列表接口,获取好友的信息
      • 然后再利用私信好友的漏洞,诱骗用户给每个好友发送指向网站B的信息
      • 只要有好友查看了这条私信信息里面的链接,CSRF蠕虫就会不断的传播下去

三.CSRF的接口攻击类型:

CSRF不仅针对GET请求,其他的类型的请求都可被利用来攻击

1.GET类型(最简单的)

  1. 方式1:借助一些自动发起请求的元素
    • 例如,在访问含有这个img的页面后,成功向http://wooyun.org/csrf?xx=11 发出了一次HTTP请求
// 只要设置这个图片的宽高为0,用户是根本感觉不到这个`<img>`元素存在
<img src=http://wooyun.org/csrf?xx=11 /> 
复制代码
  1. 方式2:页面里面放入一个自动提交的表单,模拟一次GET请求

  2. 方式3:页面内部自动发起一个get方法的ajax请求

2.POST类型

  1. 方式1:页面里面放入一个自动提交的表单,模拟一次POST请求
<form action=http://wooyun.org/csrf.php method=POST>
<input type="text" name="xx" value="11" />
</form>
<script> document.forms[0].submit(); </script>
复制代码
  1. 方式2:页面内部自动发起一个post方法的ajax请求
  2. 方式3:页面以GET请求发起,后台接受到后再以POST方式转发

四.规避攻击的方法:

1.后端判断referer是否合法(不推荐)

  • Referer记录了HTTP请求的来源地址
    • 一般情况下,受一个页面安全限制的请求都是来源于这个网站
    • 通过HTTP的referer可知道,用户是通过哪个网站发送这个请求的。
// koa服务器检查Referer示例:
app.use(async (ctx, next) => {
    let referer = ctx.headers.Referer;
    // 验证Referer是否是以 test.example 开头的
    if((referer != null ) && (_.startsWith(referer, "test.example")) {
        // 验证通过
        await next();
    } else {
        验证失败,返回错误
        ctx.status = 401;
        return ctx.body = {
            err: '危险的请求,拒绝访问'
        }
    }
}) 
复制代码
  • 注意: Referer的判断并不是好方法,有很多缺陷,具体可见 这里
    1. 某些浏览器,例如IE6FF2都是可以自己设置Referer值的
    2. 因为Referer会记录用户的访问记录,侵犯隐私,因此在最新的浏览器中,用户可以自己设置发送请求时不再提供Referer,这种在请求时就会被误当作CSRF攻击
    3. 判断referer是否为某域名,可以自己来伪造
      • 例如:判断Referer开头是否以 126.com 以及 126 子域名,而不验证根域名为126.com,这里就可以伪造出 x.126.com.xxx.com 的域名
    4. ...

2.请求中添加上token并验证,即校验信息不通过cookie来实现

  1. 该做法的原因
    • 要明白cookie获得携带的区别:
      • 获得cookie: 即通过js获取cookie中的参数值,有同源策略的限制
      • 携带cookie: http请求时自动携带上cookie,会自动带上该同源域下的所有cookie
    • token是csrf.com页面渲染时一起带过来的,这样的话,如果不在csrf.com页面发起这个点赞请求,不同域的网站是拿不到token的
    • 而CSRF只能通过自动携带cookie去发起攻击,,因此此方式可拦截
  2. 两种具体做法
    1. 前端记录token,在请求参数中添加token,后台判断:
    // 前端
        //登录成功后,将token保存在本地(可以是cookie方式,也可以是 localStorage 方式)
        // 然后每次请求时添加一个 token 参数
    
    // 后端,每个请求过来都验证token是否有效:
    app.use( async (ctx, next) => {
        var token = req.session.token;
        var csrfToken = req.param.csrftoken;
        if(token != null && xhrToken != null && token.equals(xhrToken)) {
            // success
            await next();
        } else {
            // error
            return error
        }
    })
    复制代码
    1. 前端记录token,在请求头中添加自定义头,存放token,后台判断:
    // expressJwt是express框架中可用的JWT校验插件
    var expressJwt = require('express-jwt');
    var validateJwt = expressJwt({ secret: config.secrets.session });
    app.use( async (ctx, next) => {
        // 也同样允许校验 token 在请求参数中的
        if(req.query && req.query.hasOwnProperty('access_token')) {
            req.headers.authorization = 'Bearer' + req.query.access_token;
        }
        // 下面会校验请求头中的 authorization 头
        validateJwt(ctx, next);
    })
    复制代码

五.注意和总结:

  1. CSRF攻击针对的是以cookie机制来验证登录权限的接口
  2. CSRF攻击不仅可以攻击get类型的接口,也可以攻击其他方法类型的接口
  3. CSRF攻击的对象,不管有没有开放同源接口访问限制,因为可以通过表单的方式发起请求,无视跨域限制
  4. 解决CSRF攻击最有效的方法就是在请求中携带token到后台去验证
    • 可以在请求参数中携带
    • 可以在请求头中携带

转载于:https://juejin.im/post/5c961d6ee51d457eab35604c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值