专栏首页多云转晴CSRF攻击与防御

CSRF攻击与防御

CSRF 攻击

跨站请求伪造(Cross-site request forgery)简称为 CSRF。这种攻击方式很奇特,它是伪造用户的请求发动攻击的,在 CSRF 攻击过程中,用户往往在不知情的情况下构造了网络请求。

CSRF 攻击者往往是利用 Cookie 进行的请求伪造,比如一个 A 站点,它有一个评论功能:

<!-- index.html -->
<body>
    <!-- POST请求,用于提交评论 -->
    <form class="wrapper" action="/comments" method="POST">
        <textarea name="msg" placeholder="..."></textarea><br />
        <button id="btn" type="submit">提交</button>
    </form>
    <div class="comment-wrapper"></div>
    <script>
        const comment = document.querySelector(".comment-wrapper");
        fetch("/getComments")   // 每次访问该页面时,都会获取到评论条目,渲染到页面上
            .then(res => res.json())
            .then(data => {
                data.data.forEach(item => {
                    let p = document.createElement("p");
                    p.textContent = item;
                    comment.appendChild(p);
                });
            }).catch(err => console.error(err));
    </script>
</body>

A 网站在用户访问站点时都会通过 Session Cookie 确定用户身份,评论在点击提交时会把评论信息发到后端,后端存储评论,然后重定向回 index.html 页面。

B 网站就发现了 A 网站评论提交有漏洞,于是在自己站点搞了一个“空”页面,代码如下:

<!-- B 网站的网页内容 -->
<body>
    <style>
        iframe,form{
            opacity: 0;
        }
    </style>
    <!-- form 元素的 target属性可以与 iframe 的name属性关联,关联后 form表单提交跳转的页面会在 iframe 中展示 -->
    <iframe id="iframe" name="csrf"></iframe>
    <form class="wrapper" action="http://www.a.com/comments" target="csrf" method="POST">
        <textarea name="msg" placeholder="..."></textarea><br />
        <button id="btn" type="submit">提交</button>
    </form>
    <script>

        window.onload = function(){
            document.querySelector("textarea").value = "哈哈哈蛤蛤蛤";
            document.querySelector('.wrapper').submit();
        };
    </script>
</body>

当用户访问 A 站点的时候,也访问了 B 站点,而 B 网站上似乎什么都没有,一片空白。当用户再次刷新 A 网站的页面时,发现不知道什么时候自己发表了评论,评论内容粗俗不堪,完全不像平时自己的言行。

B 网站就是利用了 A 网站的 CSRF 漏洞实现了请求伪造。每个 A 网站的用户访问 B 网站时,B 网站就会伪装成用户的身份给 A 网站发送网络请求。

当用户访问 B 网站时,form 表单向 A 网站提交数据,这时会带上用户在 A 站点的 Session Cookie,这个 Cookie 是 A 网站用于验证用户身份的,结果 B 网站发出的请求也带有用户身份标识,服务器就会认为是该用户的评论信息,然后做了处理。

上面例子中展示的是 POST 请求的伪造,这算是很复杂的 CSRF 攻击手段了,如果是 GET,那就更容易了,只需一行代码就可以搞定,因为 GET 请求的数据一般都在 URL 上,只需构造一个 URL 即可。比如,一个博客网站,它用 GET 请求删除某个博文,URL 格式如下:

http://www.blog.com/delete?id=1234

id 就是博文的编号,如果知道这个编号的博文是谁发的(知道博主是谁),攻击者就可以在自己的网站的页面上写下如下的代码:

<img src='http://www.blog.com/delete?id=1234' alt='加载失败了~' />

图片的路径指向删除博客的 URL,当博主访问这个页面时,觉得什么都没有,结果自己的博文莫名其妙少了一篇。博主访问恶意网站时,会对图片的路径发起 GET 请求(图片的地址是博客网站的,请求对象是博客网站后端),此使博主在博客网站是登录态,请求就会带上 Cookie,结果后端认为就是博主本人了,就删掉了博文。

通过上面例子可以发现,CSRF 攻击可以利用表单提交、src 属性不受跨域限制发动攻击。用户往往在不知情的情况下,只是点了某个链接,就中招了。CSRF 攻击危害很大,可能删除一篇博文也没什么,但是如果是发表评论,可能就会造成恶劣的影响,比如发表一些色情甚至反动的言论栽赃用户;银行的支付系统如果存在 CSRF 漏洞,很可能造成用户财务损失;CSRF 也会损害网站的名誉。

防范 CSRF 攻击

CSRF 攻击主要特征就是利用用户的登录态,在有登录状态网站时访问了恶意网站(用户并不清楚是不是恶意的),恶意网站利用 CSRF 漏洞伪装成用户发起了请求。恶意站点之所以能伪装成用户,这是因为请求时带上了用户的身份信息(被攻击站点的 Cookies)。

恶意网站发网络请求,但不访问被攻击网站的前端。如果被攻击网站在查询伪造请求时,请求首部的 referer 是恶意网站的。就可以验证 Referer 这个请求首部来判断是否是用户发送的请求,比如使用正则表达式,匹配是不是本域下的地址。

Referer 的缺陷在于,服务器并非什么时候都能取到 Referer。很多用户出于隐私保护的考虑,限制了 Referer 的发送。在某些情况下,浏览器也不会发送 Referer,比如从 HTTPS 跳转到 HTTP,出于安全的考虑,浏览器也不会发送 Referer。

另外一种办法就是禁止第三方网站携带 Cookie。在 Cookie 中新增了一个 same-site 属性,它有两个值:StrictLax,前者只会在第一方上下文中发送,不会与第三方网站发起的请求一起发送;后者允许与顶级域一起发送,并将与第三方网站发起的 GET 请求一起发送。这是浏览器中的默认值。如:

Set-Cookie: SameSite=Lax

SameSite 很好用,但是兼容性不太好。

加入验证信息

CSRF 攻击并不会访问目标网站的前端,因此可以在前端加入验证内容,即必须或只能在目标网站前端中才能获得的信息,如果不访问前端就不能请求成功。加入验证信息一般有两种方案,一种是使用图形验证码,在提交信息之前,需要先输入图像验证码,验证码是随机生成的,因此在恶意网站是不能知道当前验证码的内容的;另一种方案是在页面中放入一个 Token,在提交内容时,必须带上这个 Token

当我们查看别人网站的 form 表单时,发现里面会有一个隐藏的输入框。

<input type="hidden" name="token" value="xxxx" />

这个输入框是专门用来存放 token 的,token 一般是一个字符串,由服务器随机生成,渲染到页面中。而攻击者不会访问前端,也就拿不到 token。除此之外,生成的 Token 还需要放在 Cookie 或者用户的 Session 中。当客户端发起请求时就会带上输入框中的 token,服务端拿到表单中的 token,然后解析出 cookie 中的 token,两者作比较,如果相等则认为是合法的。

之所以将 token 存两个地方,是因为:如果只有表单中的 token 时,恶意网站就可以随意捏造,而如果只有 cookies 中有 token,也不行,请求时就会把这个 token 带过去。当两者都有时,攻击者无法获知表单中的 token,也无法改变 cookie 中的 token。当两者一致时才认为合法。

token 与 验证码相比对用户体验比较友好,验证码还需要用户手动输入,有时老是输入错误,体验就不好了。如果 Token 保存在 Cookie 中,而不是服务器端的 Session 中,则会带来一个新的问题。如果一个用户打开几个相同的页面同时操作,当某个页面消耗掉 Token 后,其他页面的表单内保存的还是被消耗掉的那个 Token,因此其他页面的表单再次提交时,会出现 Token 错误。在这种情况下,可以考虑生成多个有效的 Token,以解决多页面共存的场景。使用 Token 时应该注意 Token 的保密性,Token 如果出现在某个页面的 URL 中,则可能会通过 Referer 的方式泄露。

点击劫持

点击劫持是一种视觉上的欺骗手段。攻击者使用一个透明的、不可见的 iframe,覆盖在一个网页上,然后诱使用户在该网页上进行操作,此时用户将在不知情的情况下点击透明的 iframe 页面。通过调整 iframe 页面的位置,可以诱使用户恰好点击在 iframe 页面的一些功能性按钮上,比如提交表单。点击劫持需要对页面布局,调整按钮的位置,引导用户点击。隐藏 iframe 很容易,设置 opacity0,或者让 iframe 设置成绝对定位,把 z-index 设置很多,让它处在最上层。

点击劫持

预防点击劫持

目标网站可以通过 iframe 嵌套到另一个网站,这是点击劫持攻击的前提。如果要防御点击劫持,需要让目标网站的网页不能内嵌。

第一种方法是使用 JavaScript 禁止内嵌。

如果一个网站没有内嵌网页,它的 top 对象与 window 对象是同一个,如果嵌套了一个网页,切换到 iframe 的环境中,会发现 top 与 window 不是一个对象,top 指向主窗口(顶级窗口),window 指向 iframe。两者的 location 对象也不一样。下面是禁止 iframe 嵌套的代码:

if(top.location !== window.location){
    // 两者不相等时,会从点击劫持的页面跳转到目标网页
    top.location = window.location;
}

在 chrome 浏览器中可以点击下面的按钮切换 iframe 环境。

切换

使用 JavaScript 禁用 iframe 嵌套存在一些缺陷,在 HTML5 中 iframe 新增了一个 sandbox 属性,它可以对呈现在 iframe 中的内容启用一些额外的限制条件。比如 allow-forms 属性值,表示禁止脚本运行,但可以提交表单。这时候,目标网站使用 JavaScript 禁止嵌套的代码就会失效。

关于 sandbox 的更多用法,可以参考 MDN:iframe sandbox[1]

X-Frame-Options

这是一个 HTTP 响应头,专门用于防止点击劫持攻击。他有三个可选值:

  • deny 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许;
  • sameorigin 表示该页面可以在相同域名页面的 frame 中展示;
  • allow-from https://example.com/ 表示该页面可以在指定来源的 frame 中展示;

X-Frame-Options 很好用,兼容性也很好,IE8 是支持的。

参考资料

[1]

iframe sandbox: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/iframe

本文分享自微信公众号 - Neptune丶(Neptune_mh_0110),作者:多云转晴丶

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • CSRF攻击与防御

      CSRF的全名为Cross-site request forgery,它的中文名为 跨站请求伪造(伪造跨站请求【这样读顺口一点】)

    那一叶随风
  • CSRF攻击与防御

    CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性,你可以这样来理解: ...

    HACK学习
  • 【安全系列】CSRF攻击与防御

    攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作。对于CSRF而言,它的攻击有两个关键点,跨站...

    深海鱼
  • CSRF攻击与防御,Web安全的第一防线(源码,实战,5分钟科普文)

    跨站请求伪造,也被称为“OneClick Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。

    HACK学习
  • Web前端安全策略之CSRF的攻击与防御

    接着上一篇文章,本篇文章我们继续来讲解前端如何处理网站的安全问题,本文主要讲解跨站请求伪造(CSRF)。

    @零一
  • CSRF攻击防御原理

    Leafo老师基于Moonscript语言开发的WEB框架Lapis,框架中有一段针对CSRF(Cross—Site Request Forgery)的防护代码...

    糖果
  • csrf攻击原理和防御

    上面的代码意思大概是,如果有username的存在,那么说明用户已经登录成功,否则通过判断url中是否有login参数,如果有就进行登录,并写cookie。该代...

    theanarkh
  • Web Security 之 CSRF

    在本节中,我们将解释什么是跨站请求伪造,并描述一些常见的 CSRF 漏洞示例,同时说明如何防御 CSRF 攻击。

    凌虚
  • 前端安全问题之-CSRF攻击

    游魂

扫码关注云+社区

领取腾讯云代金券