写点什么

就一次!带你彻底搞懂 CSRF 攻击与防御

  • 2024-08-09
    湖南
  • 本文字数:4858 字

    阅读完需:约 16 分钟

与 XSS 攻击相比,利用 CSRF 漏洞发动攻击会比较困难,这也是在网络上看起来 CSRF 的人气小于 XSS 的原因之一。下面我们来利用 CSRF 漏洞发起攻击,并针对攻击进行防御,彻底弄懂 CSRF,话不多说,我们直接开冲。

什么是 CSRF 攻击

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF,是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。----来自维基百科


简单来说就是黑客冒充你执行一些恶意行为。中文名叫跨站伪造请求,乍一看不明所以,我们拆开来看就明白了:

  • 跨站: 跨站可以理解为你登录了 A 网站,然后访问了恶意 B 网站。

  • 请求: HTTP 请求。

  • 伪造: 欺骗 A 网站认为操作请求是用户发出的。


对比 XSS 可以影响所有访问被攻击页面的用户,CSRF 攻击影响的范围一般局限于用户的账户和应用有哪些功能可以被攻击操作,且依赖用户的登录状态。

攻击流程

CSRF 攻击原理与流程如下:

  1. 用户登录了 A 网站(可能是银行、邮箱这类极具黑客吸引力的网站)。

  2. 黑客诱导用户访问含有恶意代码的 B 网站(一般是美女图片、一块钱抽华人牌 2060 款手机等恶意链接,让人忍不住想点哈哈哈)。

  3. B 网站要求浏览器向 A 网站发送 GET、POST 等恶意请求。

  4. 用户没有退出 A 网站情况下,恶意请求自动携带用户的身份认证信息(Cookie、Session ID 等)。

  5. A 网站确认了用户凭证,误以为是用户的请求以用户的名义执行了恶意操作。(黑客因此会转走你的钱、通过邮箱收发你其他网站的验证码、严重点的还可以让你自动给我的文章点赞、收藏哈哈哈)。


从流程可以得知,发动 CSRF 攻击需要如下前提:

  • 网站存在黑客有想法的功能请求。(转账、修改密码等)

  • 执行操作通过 HTTP 请求,且网站只依靠会话 Cookie 来认证用户。

  • 请求参数都可以构造的,没有无法提前获知的随机字符串。

  • 用户在登录正常网站后,没有退出访问了恶意网站。

如何利用 CSRF 漏洞进行攻击

根据 CSRF 的攻击类型可以分为:GET 型、POST 型等。


  • GET 类型 CSRF

在 GET 型 CSRF 攻击中,恶意请求通过 URL 参数发送。如果开发者网络安全意识不够,可能会误用 GET 请求执行敏感操作,这就给黑客留下了可乘之机。先说结论,再生产环境中不要使用使用 GET 发送敏感操作的请求。


一般黑客会构建一个链接请求然后发送给用户,直接发送链接让用户进入的话会容易被看穿,一般是伪装到网站的图片中,只需要诱导用户访问该伪装过的网站,网站就会自动发起图片 src 中的 HTTP 请求,如下该请求会携带 bank 域名下的 Cookie 认证信息。

<img src="http://bank.com/transfer?to=hacker&amount=10000000" />
复制代码
  • POST 类型 CSRF

在 POST 类型中,一般是构建一个看不见的 POST 表单,恶意请求通过表单参数发送。


黑客会构建一个 POST 表单,然后还是老办法诱导用户访问这个页面,用户访问该页面后,页面的表单自动提交发起一次 POST 请求。

<form action="http://bank.com/transfer" method="POST" style="display:none;">   <input type="hidden" name="to" value="hacker">   <input type="hidden" name="amount" value="10000000"></form><script>   document.forms[0].submit();</script>
复制代码

CSRF 攻击实操

下面我们来玩一个 CSRF 攻击的小实验。以下实验由 portswigger 靶场提供,请不要随意去用 CSRF 漏洞攻击别人的网站哦。


进入网站,我们发现这个网站是一个 BLOG 站点,页面上就一个主页、一个登录入口。


依据实验提示给出的账号密码,我们登录这个站点。


登录完成后,我们看到他的网站有一个修改邮箱的功能,更改这个邮箱,打开控制台,我们发现他通过一个 POST 表单请求来实现修改邮箱,黑客就喜欢对这种功能发起 CSRF 攻击,一旦他绑定了自己的邮箱,基本就拿到账号的最高权限了。


通过上面的学习,我们知道可以通过构造请求来假冒用户执行操作,那么我们可以针对这个功能做点文章,构建一个 POST 表单,分发给受害人。


通过查看表单的元素结构,我们复制这个表单到一个新建 html 文件中,并且补齐 action 的路径(0a6a007b041a3cac83ce838d004f0011.web-security-academy.net/my-account/…)

随后删除多余内容,value 处填写我们想要的邮箱。(黑客一般会表单给表单设置隐藏,我们这里做实验就把表单显示出来了)

<html>
<body>
<form class="login-form" name="change-email-form" action="https://0a6a007b041a3cac83ce838d004f0011.web-security-academy.net/my-account/change-email" method="POST"> <input required="" type="email" name="email" value="hacker@normal-user.net"> <button class="button" type="submit"> sub</button></form>
<script>document.forms[0].submit(); </script>
</body></html>
复制代码

点击上方进入漏洞服务器。

将我们刚刚构建好的 html 复制进 body 中,点击 store 保存,随后点击 Deliver exploit to victim 分发给受害人。

页面上方出现 Congratulations, you solved the lab!,说明我们攻击成功。

点击预览漏洞,可以查看我们的攻击效果。


预览攻击后,我们发现页面上的邮箱已经被替换成我们想要的邮箱。

至此 CSRF 攻击实验成功进行,黑客绑定了他的邮箱,拿到了该账号的最高权限,基本上可以为所欲为了(害怕)。


我们总结一下攻击流程:

  1. 首先,找到想要攻击的网站功能,分析修改邮箱的请求,组装我们的请求表单

  2. 将表单分发给 blog 用户,诱导受害人点击

  3. blog 用户访问我们的网站,表单携带 blog 网站下的 cookie 自动提交

  4. 最后,邮箱修改成功


该实验的链接CSRF攻击实验


该网站还有其他 CSRF 攻击的实验,知道了 CSRF 攻击的原理,有兴趣的同学可以尝试着解决。

如何预防 CSRF 攻击


由上方 CSRF 的攻击方式,我们可以总结出 CSRF 攻击的如下特点:

  • 依赖于浏览器的自动发送凭据,黑客不能读取凭证,但是可以借用凭证冒充用户,有点伪装的意思了。

  • 攻击请求通常从黑客控制的站点发出。

  • 攻击非常隐蔽,用户在不知情的情况下触发恶意请求。


那么根据它的特点我们可以做如下防御:

验证 Referer 和 Origin 头

检查请求的 Referer 和 Origin 头,以确保请求来源于受信任的域。

  • Referer 头:包含请求的来源 URL。

  • Origin 头:包含请求的来源域。


在 koa 中验证 Referer 和 Origin 头:


在 koa 中可以将验证 Referer 和 Origin 头封装成一个中间件,在处理接口的回调之前使用即可:

const Koa = require('koa');const Router = require('koa-router');const bodyParser = require('koa-bodyparser');
const app = new Koa();const router = new Router();
const trustedOrigins = ['https://trusted-domain.com'];
function checkRefererAndOrigin(ctx, next) { const referer = ctx.headers.referer; const origin = ctx.headers.origin;
const isRefererValid = referer && trustedOrigins.some(trustedOrigin => referer.startsWith(trustedOrigin)); const isOriginValid = origin && trustedOrigins.includes(origin);
if (isRefererValid || isOriginValid) { return next(); } else { ctx.status = 403; ctx.body = 'Forbidden: CSRF token validation failed'; }}
router.post('/transfer', checkRefererAndOrigin, async (ctx) => { // 处理转账请求 ctx.body = 'Transfer successful';});
app .use(bodyParser()) .use(router.routes()) .use(router.allowedMethods());
app.listen(3000, () => { console.log('Server running on http://localhost:3000');});
复制代码

不过这两种方式并不太可靠。


首先 Referer 头可能被移除或篡改,攻击者可以通过工具(如代理服务器)伪造 Referer 头,从而绕过服务器的验证。根据 Referer 的转发策略可以设置 same-origin 限制同源时才转发 referer,这里设置策略又给了黑客可乘之机,黑客可以设置 no-referrer 不转发,这样就无法验证了。


其次 Origin 头只包含域名不包含路径与 query,并且由于不同浏览器的实现不同,导致这个表现出来的特性也不一样(IE 说的就是你)。

使用 SameSite 属性的 Cookie

通过将 Cookie 设置为 SameSite,可以限制浏览器在跨站请求中发送 Cookie,从而防御 CSRF 攻击。为了从源头解决问题,Google 出手了。


  • SameSite=Strict:最严格的模式,Cookie 在任何跨站请求中不会被发送。就是 A 网站请求 B 网站时,B 网站的 Cookie 不会被携带。

Set-Cookie: sessionid=abc123; SameSite=Strict
复制代码
  • SameSite=Lax:稍微宽松的模式,Cookie 在大多数跨站请求中不会被发送,但在 GET 请求导航到目标网站时会被发送。( Chrome 80 版本起,Chrome 更新了 SameSite 属性的默认值,由 None 改成了 Lax)

Set-Cookie: sessionid=abc123; SameSite=Lax
复制代码
  • SameSite=None: 无限制模式,Cookie 在跨站请求时也会发送。为了使用此模式,必须设置 Secure 属性(即 Cookie 只能通过 HTTPS 发送)。

Set-Cookie: sessionid=abc123; SameSite=None
复制代码

koa 中设置 SameSite:

const Koa = require('koa');const Router = require('koa-router');
const app = new Koa();const router = new Router();
router.get('/', (ctx) => { ctx.cookies.set('example', 'value', { maxAge: 1 * 60 * 60 * 1000, // cookie的有效时长, 这里为1小时,单位是毫秒 httpOnly: true, // Cookie只通过HTTP(S)传输,客户端JavaScript无法访问 sameSite: 'Strict', // 可以是 'Strict', 'Lax' 或 'None' secure: true // 设置为 true 时,Cookie 只能通过 HTTPS 传输(SameSite 为 'None' 时必须设置) }); ctx.body = 'Cookie 设置完毕';});
app .use(router.routes()) .use(router.allowedMethods());
app.listen(3000, () => { console.log('Server running on http://localhost:3000');});
复制代码

关于 chromium 内核 SameSite 版本策略可以看这里 cookie-legacy-samesite-policies。


合理设置了 SameSite 后,可以从源头防范一部分 CSRF 攻击。当然这也需要浏览器支持 Google 这个 SameSite 提案,好消息是各大现代浏览器现在都支持了 SameSite 属性的 Cookie。


但仅仅通过 SameSite 属性控制第三方网站访问 Cookie 的一层保护显然是不够的,还需要结合其他方案,下面我们来介绍通过 CSRF Token。

CSRF Token

因为 CSRF 就是利用了浏览器在发起请求时会自动携带 cookie 的特性,那我们再在每个用户请求中包含一个唯一且随机生成的令牌,以验证请求的合法性。


具体流程:

  1. 在用户登录成功时,返回一个 token 给到前端,前端将这个 token 存储在本地 localStorage 中。

  2. 后续每次在发送请求前,从 localStorage 取出放到请求头中。

  3. 后端收到请求后,首先验证 token 值。正常请求由应用程序附上 token 值,正常放行。而黑客发起的 CSRF 攻击不包含这个 token 值,后端拒绝请求。


这样给每一个请求加上 token 后,在没有 XSS 漏洞的情况下, CSRF 攻击就可以有效防御了。

验证码

因为 CSRF 攻击的是系统的功能,那么我们在执行敏感操作(如资金转移、用户资料修改)时给用户的设备发送一个验证码,黑客拿不到验证码也就无法发动攻击啦。亦或者用户在执行一些敏感操作时,要求用户再次输入一遍密码,黑客无从得知密码,CSRF 攻击也就被防御了。


其实验证码的思想和 token 的思想差不多,都是增加了一个字段对用户进行认证校验,结合多种防御手段就可以有效地将 CSRF 攻击拒之门外啦。

总结

通过本文章的学习,相信大家都对 CSRF 的攻击与防御措施有了一个了解,我们简单总结如下:

  • 用户打开正常网站时,访问了恶意网站,此时就可能发生 CSRF 攻击。

  • 黑客可以通过图片 URL、超链接、提交表单方式发起攻击。

  • 可以通过 Referer、Origin 头、SameSite、CSRF Token、验证码方式防御 CSRF 攻击。

  • 不要通过 GET 请求去执行一些操作,规范使用 GET,仅将其当做获取数据的请求方式,提高 CSRF 攻击门槛。


另外,作为用户我们可能会实在忍不住想要访问一些美女网站时或者点击未知链接时,建议大家使用一个平时不使用的浏览器去访问,来降低风险。


对于 CSRF 攻击与防御的内容截止目前就结束了,感谢大家的耐心阅读。


作者:Sny

链接:https://juejin.cn/post/7400566246436126770

用户头像

欢迎关注,一起学习,一起交流,一起进步 2020-06-14 加入

公众号:做梦都在改BUG

评论

发布
暂无评论
就一次!带你彻底搞懂CSRF攻击与防御_黑客_我再BUG界嘎嘎乱杀_InfoQ写作社区