如何在Express/MongoDB项目的CTF场景中用Axios传递会话Cookie
需求背景
正在做一个包含CSRF漏洞的CTF项目,核心逻辑是:用户向后端提交指定URL后,后端要以support用户的身份(携带其会话Cookie)向该URL发起请求,以此模拟CSRF攻击场景。
现有代码(已移除无关部分)
const express = require('express') const session = require('express-session') const MongoDBSession = require('connect-mongodb-session')(session) const axios = require('axios') const mongoose = require('mongoose') const app = express() const CreateDefaultUsers = () => { try { User.create({ username: "guest", password: "guest" }) User.create({ username: "support", password: "639Mydd&ou6Y" }) console.log("User Created...") } catch (err) { console.log("Error creating users") } } const db = mongoose.connection db.on('error', (error) => console.log(error)) db.once('open', () => { console.log('Connected to Database') CreateDefaultUsers() }) const store = new MongoDBSession({ uri: mongoURI, collection: 'csrf-sessions' }) app.use(session({ name: "SESS_COOKIE", secret: 'secret', resave: false, saveUninitialized: false, store: store, cookie: { maxAge: 1000 * 60 * 60 * 24, secure: false, httpOnly: false, sameSite: false } })) app.post('/api/login-support', async (req, res) => { console.log(req.session) const user = await User.findOne({ username: req.body.username, password: req.body.password }) if (user) { req.session.userId = user._id.toString(); res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000") res.send(req.session) } else { res.status(403).json({ status: "Invalid Login" }); } }) app.post('/api/support', async (req, res) => { axios.post('http://localhost:5000/api/login-support', { username: "support", password: "639Mydd&ou6Y" }, { withCredentials: true }) .then((data) => axios.get(req.body.url, { headers: { Cookie: data } }, { withCredentials: true }) .then((data) => console.log(data))) }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) })
问题分析
当前请求无法携带support用户Cookie的原因:
- Cookie获取错误:
axios.post返回的data是接口返回的session对象,不是响应头里的Set-Cookie字段,直接把它作为Cookie值完全无效。 - Axios配置错误:第二个请求的
withCredentials和headers分属不同配置对象,Axios不会识别,应该合并到同一个配置里。 - 会话管理冗余:每次收到
/api/support请求都重新登录support用户,没必要且容易出现会话不一致问题。
修复方案(修正Cookie携带逻辑)
修改/api/support接口代码,正确提取并携带Cookie:
app.post('/api/support', async (req, res) => { try { // 登录support用户,获取会话Cookie const loginRes = await axios.post( 'http://localhost:5000/api/login-support', { username: "support", password: "639Mydd&ou6Y" }, { withCredentials: true } ) // 从响应头提取会话Cookie(只取键值对部分) const sessionCookie = loginRes.headers['set-cookie']?.find(c => c.startsWith('SESS_COOKIE='))?.split(';')[0] if (!sessionCookie) { console.log("Failed to get support session cookie") return res.status(500).send("Internal error") } // 携带Cookie请求用户指定URL const targetRes = await axios.get(req.body.url, { headers: { Cookie: sessionCookie }, withCredentials: true }) console.log(targetRes.data) res.send("Request sent successfully") } catch (err) { console.error(err) res.status(500).send("Request failed") } })
更优实现思路
如果想要更贴近真实CSRF场景(模拟用户浏览器行为),推荐使用Puppeteer(无头浏览器)替代Axios:
- 无头浏览器会自动处理会话Cookie,无需手动提取设置,完全符合真实用户的浏览器行为。
- 可模拟点击、表单提交等操作,适合构造复杂CSRF payload场景。
示例代码片段:
const puppeteer = require('puppeteer') app.post('/api/support', async (req, res) => { try { const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage() // 登录support用户,浏览器自动保存Cookie await page.goto('http://localhost:5000/login') // 假设存在登录页面 await page.type('#username', 'support') await page.type('#password', '639Mydd&ou6Y') await page.click('#login-btn') await page.waitForNavigation() // 访问用户指定URL,自动携带会话Cookie await page.goto(req.body.url) const content = await page.content() console.log(content) await browser.close() res.send("Request sent successfully") } catch (err) { console.error(err) res.status(500).send("Request failed") } })
另外可优化会话管理:服务器启动时提前登录support用户并保存其会话Cookie,后续请求直接复用,避免重复登录提升效率。
内容的提问来源于stack exchange,提问作者Josh Morrison
相关产品推荐
相关产品推荐

