一文带你学习跨站点请求伪造(CSRF)

一文带你学习跨站点请求伪造(CSRF)

1.何为CSRF

CSRF的全名是Cross Site Request Forgery,翻译成中文就是跨站点请求伪造

CSRF是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨站脚本(XSS)相比, XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任

例如现在有一个网站,只需要请求这个URL,就能够把编号为”156713012″的博客文章删除。

http://blog.sohu.com/manage/entry.do? m=delete&id=156713012

攻击者首先在自己的域构造一个页面:

其内容为:

<img src="http://blog.sohu.com/manage/entry.do? m=delete&id=156714243" />

攻击者诱使目标用户访问这个页面,从而执行CSRF攻击

该用户看到了一张无法显示的图片,但是不幸的式此时该用户的博客已经被删除

一文带你学习跨站点请求伪造(CSRF)

回顾整个攻击过程,攻击者仅仅诱使用户访问了一个页面,就以该用户身份在第三方站点里执行了一次操作。

这个删除博客文章的请求,是攻击者所伪造的,所以这种攻击就叫做”跨站点请求伪造”。

CSRF漏洞的根本是:简单的身份验证只能保证请求是发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的💄

2.浏览器的Cookie策略

浏览器所持有的Cookie分为两种:一种是”Session Cookie”,又称”临时Cookie”;另一种是”Third-party Cookie”,也称为”本地Cookie”。

两者的区别在于,Third-party Cookie是服务器在Set-Cookie时指定了Expire时间,只有到了Expire时间后Cookie才会失效,所以这种Cookie会保存在本地;而Session Cookie则没有指定Expire时间,所以浏览器关闭后,Session Cookie就失效了。

例如:

 php
header("Set-Cookie: cookie1=123; ");
header("Set-Cookie: cookie2=456; expires=Thu, 01-Jan-2030 00:00:01 GMT; ", false);
?>

IE出于安全考虑,默认禁止了浏览器在 <img>&#x3001;<iframe>、<script>、<link></code>等标签中发送第三方Cookie</strong><br><strong>攻击者则需要精心构造攻击环境,比如诱使用户在当前浏览器中先访问目标站点,使得Session Cookie有效,再实施CSRF攻击❌</strong></p></blockquote><p>在当前的主流浏览器中,默认会拦截Third-party Cookie的有:IE 6、IE 7、IE 8、Safari;不会拦截的有:Firefox 2、Firefox 3、Opera、Google Chrome、Android等。</p><h1><a id="3P3P_60"></a>3.P3P头的副作用</h1><p>浏览器拦截第三方Cookie的发送,在某种程度上来说降低了CSRF攻击的威力。可是这一情况在"P3P头"介入后变得复杂起来。</p><p>P3P Header是W3C制定的一项关于隐私的标准,全称是The Platform for Privacy Preferences。</p><blockquote><p><strong>如果网站返回给浏览器的HTTP头中包含有P3P头,则在某种程度上来说,将允许浏览器发送第三方Cookie。在IE下即使是<code><iframe>、<script></code>等标签也将不再拦截第三方Cookie的发送💍</strong></p></blockquote><p>在网站的业务中,P3P头主要用于类似广告等需要跨域访问的页面。</p><p>正因为P3P头目前在网站的应用中被广泛应用,因此在CSRF的防御中不能依赖于浏览器对第三方Cookie的拦截策略,不能心存侥幸。</p><p>很多时候,如果测试CSRF时发现<code><iframe></code>等标签在IE中居然能发送Cookie,而又找不到原因,那么很可能就是因为P3P头在作怪。</p><h1><a id="4CSRF_76"></a>4.CSRF攻击流程</h1><p><img src="https://img-blog.csdnimg.cn/eda9087c37624f65a4d8da4ffa7fa666.png"></p><p>具体的攻击流程如下:</p><ol><li>用户正常登录web服务,并一直保持在线</li><li>服务器返回用户凭证Session ,并将其保存在Cookie中</li><li>攻击者生成payload,并放置在用户可访问的地方</li><li>攻击者诱导用户点击在第3步放置的链接,此时用户一直在线,且是用同一浏览器打开(保证Cookie未失效)</li><li>用户点击恶意链接</li><li>恶意链接向服务器请求,由于用户Cookie未失效,就携带用户Cookie访问服务器</li><li>服务器收到请求,此时用户Cookie 未失效,并判定为"用户"发起的正常请求,并做出响应</li></ol><h1><a id="5CSRF_96">;</a>5.CSRF的分类</h1><h2><a id="GET_98"></a>GET型</h2><p>在web应用中,很多接口通过GET进行数据的请求和存储,如果未对来源进行校验,并且没有token保护,攻击者可以直接通过发送含有payload的链接进行诱导点击;亦可以通过评论区或类似功能处发布图片,通过修改img地址的方式保存至页面,用户访问便会进行自动加载造成攻击</p><pre><code class="prism language-html">
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://bank.example.com/withdraw?amount=1000&to=Bob<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
</code></pre><h2><a id="POST_105"></a>POST-表单型</h2><p>首先,测试时,为了扩大危害,可以尝试将POST数据包转换成GET数据包,后端如果采用例如<code>@RequestMaping("/")</code>这种同时接受POST和GET请求的话,就可以成功</p><p>利用起来无非也是构造一个自动提交的表单,然后嵌入到页面中,诱导受害者访问,受害者访问后会自动提交表单发起请求</p><pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>http://bank.example.com/csrf</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>POST</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>amount<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1000<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><!--</span-->form</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> document<span class="token punctuation">.</span>forms<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">submit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><!--</span-->script</span><span class="token punctuation">></span></span>
</code></pre><h2><a id="POSTJSON_118"></a>POST-JSON型</h2><p>如果我们发现请求头中的<code>Content-Type</code>值是<code>application/json</code>,基本上就可以确定采用了前后端分离了</p><p>这种一般有4种利用手法:</p><ul><li>json转param</li></ul><p>部分网站可能同时支持json和表单格式,所以我们可以尝试进行转换,如把 <code>{"a":"b"}</code> 转换为 <code>a=b</code>,服务端可能也会解析</p><ul><li>闭合JSON</li></ul><p>构造闭合语句,比较鸡肋</p><ul><li>ajax发起请求</li></ul><p>当跨域影响用户数据HTTP请求(如用XMLHttpRequest发送get/post)时,浏览器会发送预检请求(OPTIONS请求)给服务端征求支持的请求方法,然后根据服务端响应允许才发送真正的请求。</p><pre><code class="prism language-sql">HTTP<span class="token operator">/</span><span class="token number">1.1</span> <span class="token number">200</span> OK
Server: Apache<span class="token operator">-</span>Coyote<span class="token operator">/</span><span class="token number">1.1</span>
Access<span class="token operator">-</span>Control<span class="token operator">-</span>Allow<span class="token operator">-</span>Origin: http:
Access<span class="token operator">-</span>Control<span class="token operator">-</span>Allow<span class="token operator">-</span>Credentials: <span class="token boolean">true</span>
Access<span class="token operator">-</span>Control<span class="token operator">-</span>Max<span class="token operator">-</span>Age: <span class="token number">1800</span>
Access<span class="token operator">-</span>Control<span class="token operator">-</span>Allow<span class="token operator">-</span>Methods: POST
Access<span class="token operator">-</span>Control<span class="token operator">-</span>Allow<span class="token operator">-</span>Headers: content<span class="token operator">-</span><span class="token keyword">type</span><span class="token punctuation">,</span>access<span class="token operator">-</span>control<span class="token operator">-</span>request<span class="token operator">-</span>headers<span class="token punctuation">,</span>access<span class="token operator">-</span>control<span class="token operator">-</span>request<span class="token operator">-</span>method<span class="token punctuation">,</span>accept<span class="token punctuation">,</span>origin<span class="token punctuation">,</span>x<span class="token operator">-</span>requested<span class="token operator">-</span><span class="token keyword">with</span>
Content<span class="token operator">-</span>Length: <span class="token number">0</span>
<span class="token keyword">Date</span>: Wed<span class="token punctuation">,</span> <span class="token number">11</span> Mar <span class="token number">2015</span> <span class="token number">05</span>:<span class="token number">16</span>:<span class="token number">31</span> GMT
</code></pre><p>然而如果服务端对<code>Content-Type</code>进行校验,则不会响应这个OPTIONS请求,从而利用失败。但是更多的情况下服务端可能不会校验<code>Content-Type</code>,或者不会严格校验<code>Content-Type</code>是否为<code>application/json</code>,所以很多情况下这是可用的</p><p>payload:</p><pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
windows<span class="token punctuation">.</span><span class="token function-variable function">onload</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">var</span> xhr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
xhr<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"POST"</span><span class="token punctuation">,</span> <span class="token string">"http://test.example.com/csrf"</span><span class="token punctuation">)</span>
xhr<span class="token punctuation">.</span><span class="token function">setRequestHeader</span><span class="token punctuation">(</span><span class="token string">"Accept"</span><span class="token punctuation">,</span> <span class="token string">"*/*"</span><span class="token punctuation">)</span>
xhr<span class="token punctuation">.</span><span class="token function">setRequestHeader</span><span class="token punctuation">(</span><span class="token string">"Accept-Language"</span><span class="token punctuation">,</span> <span class="token string">"zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"</span><span class="token punctuation">)</span>
xhr<span class="token punctuation">.</span><span class="token function">setRequestHeader</span><span class="token punctuation">(</span><span class="token string">"Content-Type"</span><span class="token punctuation">,</span> <span class="token string">"application/json; charset=utf-8"</span><span class="token punctuation">)</span>
xhr<span class="token punctuation">.</span>withCredentials <span class="token operator">=</span> <span class="token boolean">true</span>
xhr<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string-property property">"a"</span><span class="token operator">:</span><span class="token string">"b"</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><!--</span-->script</span><span class="token punctuation">></span></span>
</code></pre><ul><li>flash+307跳转</li></ul><p>flash已经被淘汰了</p><h1><a id="6CSRF_173"></a>6.CSRF的快速验证</h1><p>非JSON的情况下,使用burp可以快速生成POC,也可以自己写,反正原理都是发起请求即可</p><p><img src="https://img-blog.csdnimg.cn/4b0b79bc9f9d4a728e9de0ff4c962528.png"></p><p>修改参数,之后通过BP的浏览器进行测试:</p><p><img src="https://img-blog.csdnimg.cn/f72e5ab748f145a4a854b7d65effec7c.png"></p><p>访问BP生成的网址:</p><p><img src="https://img-blog.csdnimg.cn/01b8dfa2d92141db8996222b5d94cdfb.png"></p><p>提交,可以看到数据已经被修改了,代表存在CSRF漏洞:</p><p><img src="https://img-blog.csdnimg.cn/51ef0605d7cf4b9cb8a4ea463b8c6ccb.png"><br><img src="https://img-blog.csdnimg.cn/65c2ad15927f4fc4bd51665de9fa08eb.png"></p><h1><a id="7CSRF_202">;</a>7.CSRF的防御</h1><h2><a id="_204"></a>验证码</h2><p>验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。</p><p>CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码,则强制用户必须与应用进行交互,才能完成最终请求。因此在通常情况下,验证码能够很好地遏制CSRF攻击。但是验证码并非万能。</p><p>很多时候,出于用户体验考虑,网站不能给所有的操作都加上验证码。因此,验证码只能作为防御CSRF的一种辅助手段,而不能作为最主要的解决方案。</p><h2><a id="Referer_Check_211"></a>Referer Check</h2><p>Referer Check在互联网中最常见的应用就是"防止图片盗链"。同理,Referer Check也可以被用于检查请求是否来自合法的"源"。</p><p>比如一个"论坛发帖"的操作,在正常情况下需要先登录到用户后台,或者访问有发帖功能的页面。在提交"发帖"的表单时,Referer的值必然是发帖表单所在的页面。如果Referer的值不是这个页面,甚至不是发帖网站的域,则极有可能是CSRF攻击。</p><blockquote><p><strong>无法依赖于Referer Check作为防御CSRF的主要手段(黑客可能篡改Referer)。但是通过Referer Check来监控CSRF攻击的发生,倒是一种可行的方法💎</strong></p></blockquote><h2><a id="Anti_CSRF_Token_218"></a>Anti CSRF Token</h2><p>CSRF为什么能够攻击成功?其本质原因是重要操作的所有参数都是可以被攻击者猜测到的。</p><p>出于这个原因,可以想到一个解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值。</p><p>这样,在攻击者不知道salt的情况下,是无法构造出这个URL的,因此也就无从发起CSRF攻击了</p><p>比如,一个删除操作的URL是:</p><pre><code class="prism language-java">http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>host<span class="token operator">/</span>path<span class="token operator">/</span>delete<span class="token operator">?</span> username<span class="token operator">=</span>abc<span class="token operator">&</span>item<span class="token operator">=</span><span class="token number">123</span>
</code></pre><p>把其中的username参数改成哈希值:</p><pre><code class="prism language-java">http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>host<span class="token operator">/</span>path<span class="token operator">/</span>delete<span class="token operator">?</span> username<span class="token operator">=</span><span class="token function">md5</span><span class="token punctuation">(</span>salt<span class="token operator">+</span>abc<span class="token punctuation">)</span><span class="token operator">&</span>item<span class="token operator">=</span><span class="token number">123</span>
</code></pre><p>但是这个方法也存在一些问题。首先,加密或混淆后的URL将变得非常难读,对用户非常不友好。其次,如果加密的参数每次都改变,则某些URL将无法再被用户收藏。最后,普通的参数如果也被加密或哈希,将会给数据分析工作带来很大的困扰,因为数据分析工作常常需要用到参数的明文。</p><p><strong>因此,我们需要一个更加通用的解决方案来帮助解决这个问题。这个方案就是使用Anti CSRF Token。</strong></p><p>回到上面的URL中,保持原参数不变,新增一个参数Token。这个Token的值是随机的,不可预测:</p><pre><code class="prism language-java">http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>host<span class="token operator">/</span>path<span class="token operator">/</span>delete<span class="token operator">?</span> username<span class="token operator">=</span>abc<span class="token operator">&</span>item<span class="token operator">=</span><span class="token number">123</span><span class="token operator">&</span>token<span class="token operator">=</span><span class="token punctuation">[</span><span class="token function">random</span><span class="token punctuation">(</span>seed<span class="token punctuation">)</span><span class="token punctuation">]</span>
</code></pre><p>由于Token的存在,攻击者无法再构造出一个完整的URL实施CSRF攻击。</p></script></iframe>

Original: https://blog.csdn.net/Gherbirthday0916/article/details/127825618
Author: 世界尽头与你
Title: 一文带你学习跨站点请求伪造(CSRF)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/661332/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球