2iaad/csrf-exploit-lab

GitHub: 2iaad/csrf-exploit-lab

一个全栈的 CSRF 漏洞演示与修复实验室,通过模拟跨站攻击场景帮助开发者直观理解 Cookie 的 SameSite 防御机制。

Stars: 0 | Forks: 0

# CSRF 漏洞利用实验室 一个小巧的全栈实验室,用于_直观体验_跨站请求伪造(CSRF)攻击的运行过程, 随后仅通过一个 cookie 属性将其拦截:**SameSite**。 ## 快速开始 ``` git clone git@github.com:2iaad/csrf-exploit-lab.git cd csrf-exploit-lab make hosts # one-time: adds app.test + evil.test to /etc/hosts (asks sudo) make up # builds + starts everything (TLS cert is auto-generated) ``` 然后在浏览器中: 1. 打开 **https://app.test:8080** 并接受证书警告(应用)。 2. 打开 **https://app.test:4000/health** 并同样接受警告(API)。 3. 注册、登录 —— 你将进入个人资料页面。 4. 访问 **http://evil.test:9090** → 返回个人资料页面并刷新:你的 用户名现在变成了 `exploited_by_csrf`。这就是 CSRF。 下文将详细解释其原理及修复方法。 ## 包含内容 | 服务 | URL | 说明 | | ---------- | ------------------------- | --------------------------------------------------------------- | | `db` | localhost:5433 | PostgreSQL,存储用户 (`init.sql`) | | `backend` | https://app.test:4000 | Express API。身份验证为 cookie 中的 JWT。**存在漏洞。** | | `frontend` | https://app.test:8080 | 合法应用:注册 / 登录 / 个人资料(黑白界面) | | `exploit` | http://evil.test:9090 | 位于**不同站点**的 "Hello world" 页面,会伪造请求 | 只有当攻击者与应用处于**不同站点**时,攻击才有意义 —— 这就是为什么会有 两个主机名,以及为什么我们需要使用 HTTPS(`SameSite=None` 的 cookie 只有 在其同时为 `Secure` 时才被允许)。 ## 设置(仅一次) 你需要在机器上修改的**唯一**内容就是 hosts 文件 —— 将这两个 名称都指向你的本机(使用 `sudo`): ``` 127.0.0.1 app.test 127.0.0.1 evil.test ``` TLS 证书会在 Docker 内部自动为你生成(自签名)并存储在 一个命名卷中 —— 不会向你的主机写入任何内容,也不需要 openssl/mkcert。 ## 运行 ``` make up # docker compose up -d --build ``` 因为证书是自签名的,所以**首次**访问时,你必须为每个 HTTPS 源 接受一次浏览器的警告(“高级 → 继续前往”) —— 而且攻击的隐藏 iframe 会向后端发送请求,因此你也必须接受后端的证书: 1. 打开 **https://app.test:8080** → 跳过警告继续前往(应用)。 2. 打开 **https://app.test:4000/health** → 跳过警告继续前往(API)。 (这个警告是为了避免在你的主机上安装受信任的 CA 而作出的取舍。如果想要 从头重新生成证书,可以通过 `make clean` 清除卷,下一次 执行 `make up` 时就会生成全新的证书。) ## 查看攻击

1. 注册一个用户,然后登录。个人资料页面会显示你的用户名。 2. **在同一个浏览器中**(保持登录状态),在另一个 站点打开攻击者页面:**http://evil.test:9090**。它只会显示 "Hello world"。 3. 回到个人资料标签页并刷新。你的用户名现在变成了 `exploited_by_csrf`。 你从未提交过该更改 —— 是攻击者的页面这么做的,并且浏览器 自动附带了你的身份验证 cookie,因为请求发往了 `app.test`。 ``` YOU (logged in) ATTACKER PAGE BACKEND app.test:8080 evil.test:9090 app.test:4000 | | | | visit "hello world" -----> | | | | auto-submit hidden form | | | POST /api/user/username | | | username=exploited_by_csrf | | | ---------------------------> | | | cookie is SameSite=None, so | | | the browser sends it even | | | cross-site --> attack works| | | | UPDATE users | | <--- 200 OK (in iframe) ---- | (done!) | refresh profile -> "exploited_by_csrf" | ``` ## 修复漏洞 打开 [`backend/controllers/auth.controller.js`](backend/controllers/auth.controller.js), 注释掉存在漏洞的 `authCookie` 行,并取消注释 `THE FIX` 行 —— 即 将 `sameSite: 'none'` 修改为 `sameSite: 'strict'`。 ``` const authCookie = { httpOnly: true, secure: true, sameSite: 'strict', maxAge: DAY }; ``` 然后执行 `make up` 并**重新登录**(以便下发新的 cookie)。现在: - **真实应用**依然有效:`app.test:8080` → `app.test:4000` 属于*同一 站点*,因此浏览器仍会发送 `Strict` cookie。 - 位于 `evil.test` 的**攻击者**属于*不同站点*,因此浏览器会拒绝 附带该 cookie。伪造的请求在无会话的情况下到达后端 → 被 `requireAuth` 拒绝。攻击已被拦截。 这种不对称性 —— “cookie 在我自己的站点畅通无阻,但绝不会被发送到 别人的站点” —— 正是整个 SameSite 防御机制的核心。
标签:CSRF, Docker, MITM代理, Web安全, 安全防御评估, 安全靶场, 测试用例, 版权保护, 自定义脚本, 蓝队分析, 请求拦截