1. 项目概述:为什么前端JS审计是渗透测试的“破局之钥”?
在渗透测试的实战中,我们常常会遇到这样的困境:面对一个看似固若金汤的Web应用,常规的端口扫描、目录爆破、SQL注入测试轮番上阵,结果却收效甚微,报告上只能写下“未发现高危漏洞”。目标系统像一块光滑的石头,找不到任何可以下手的缝隙。这时候,很多测试人员会感到沮丧,甚至怀疑自己的技术。但我想说,问题可能不在于你的工具不够锋利,而在于你忽略了最显眼、却又最容易被轻视的“富矿”——前端JavaScript代码。
前端JS审计,正是打开这扇门的钥匙。它不再是传统渗透测试中可有可无的补充环节,而是现代Web应用安全评估中,从“黑盒”走向“灰盒”甚至“白盒”的关键一步。为什么这么说?因为现代Web应用,尤其是单页应用(SPA)和前后端分离架构,其业务逻辑、API接口、认证授权机制、甚至是敏感数据,都大量地“藏”在前端代码里。服务器端可能只负责最核心的数据处理和存储,而“怎么请求”、“请求什么”、“数据怎么展示和交互”这些规则,都写在了前端JS里。你绕过WAF、防火墙,却可能在前端一个不经意的API路径拼接里,发现一个未授权访问的入口;你苦于没有测试账号,却可能在前端源码里找到一个被注释掉的、用于测试的超级管理员令牌。
这个项目标题“前端JS审计:渗透测试的‘破局之钥’”,精准地概括了其核心价值。它不是教你写一个JS审计工具,而是系统地分享一套方法论和实战技巧,告诉你如何像阅读一本“应用说明书”一样去阅读前端JS代码,从中提取出渗透测试所需的关键情报,从而打破僵局,找到那些常规扫描无法触及的攻击面。接下来,我将结合我多年的红队和渗透测试经验,为你拆解这套“破局”之术。
2. 核心思路:从“看热闹”到“看门道”的思维转变
很多刚接触前端审计的朋友,打开浏览器的开发者工具,面对动辄上万行、经过压缩混淆的JS代码,第一反应是头晕。这很正常。前端JS审计的第一步,不是一头扎进代码里,而是完成一次思维上的根本转变:从被动的“执行者”变为主动的“侦察兵”和“架构师”。
2.1 侦察思维:将前端视为情报源
传统渗透测试的情报收集(Reconnaissance)主要针对域名、IP、端口、中间件、框架指纹等。前端JS审计则将情报收集的触角深入到了应用内部。你需要带着明确的目标去“侦察”:
- API端点侦察:应用调用了哪些后端接口?它们的URL模式是什么(
/api/v1/user,/graphql)?哪些是公开的,哪些需要认证? - 认证与授权模型侦察:用户登录后,Token是如何存储和传递的(
localStorage,sessionStorage, Cookie, Header)?有哪些角色(admin,user,guest)?权限判断的逻辑是在前端实现的吗? - 业务逻辑与数据流侦察:一个订单是如何创建的?涉及哪些步骤和API调用?用户输入的数据经过了哪些处理和校验?
- 敏感信息泄露侦察:代码里是否硬编码了API密钥、数据库连接字符串、内部服务器地址、测试账号密码、加密密钥的片段或哈希值?
- 第三方依赖侦察:应用引用了哪些第三方JS库(如jQuery, React, Vue, 以及各种功能库)?这些库的版本是否存在已知漏洞?
实操心得:不要试图一次性理解所有代码。带着“找什么”的问题去看,效率会高得多。例如,今晚的目标就是找出所有API端点,那么你的搜索关键词就是
fetch,axios,$.ajax,/api,.then,async/await等。
2.2 架构思维:理解代码组织与通信模式
现代前端项目通常使用Webpack、Vite等构建工具,代码是模块化的。审计时,你需要快速理解其项目结构(虽然经过打包,但通过Source Map或代码特征可以推断)。更重要的是理解其与后端的通信模式:
- RESTful API:这是最常见的。关注HTTP方法(GET, POST, PUT, DELETE)、状态码处理和错误信息。一个设计不当的REST API,其错误信息可能直接泄露堆栈跟踪。
- GraphQL:越来越流行。你需要找到GraphQL端点(通常是
/graphql或/gql),并尝试获取其Schema(通过Introspection查询)。GraphQL的灵活性既是优点,也容易产生信息泄露和DoS漏洞。 - WebSocket:用于实时通信。关注连接建立时的认证、消息格式以及服务端推送的数据是否包含敏感信息。
- Server-Sent Events (SSE):单向数据流,同样需要关注数据内容。
理解这些模式,能帮你快速定位到处理核心通信的代码段,而不是在UI渲染的代码里浪费时间。
2.3 攻击面思维:从信息到漏洞的转化
收集到情报后,关键在于如何将其转化为实际的攻击面。这需要你将前端代码中的“线索”与常见的Web漏洞模型关联起来。
- 找到的API端点->未授权访问/越权测试:直接访问这些端点,或修改请求参数(如用户ID)。
- 前端权限校验逻辑->逻辑绕过:如果权限判断仅在前端通过
if (user.role !== 'admin') return;实现,那么直接构造请求发给后端可能绕过。 - API请求参数的构造方式->参数污染、注入测试:观察参数是如何拼接的,是否存在拼接SQL语句或命令的可能性?是否直接信任了URL中的参数?
- 错误处理机制->信息泄露:前端是否捕获并显示了详细的服务器错误信息?
- 第三方库版本->已知漏洞利用:查询该版本库的CVE记录。
这套“侦察-架构-攻击面”的思维框架,是进行有效前端JS审计的基础。它让你从漫无目的地“看代码”,转变为有策略、有重点地“挖漏洞”。
3. 实战环境搭建与核心工具链
工欲善其事,必先利其器。前端JS审计虽然核心是“读”,但也离不开一系列工具的辅助,它们能极大提升你的效率和深度。以下是我在日常工作中高频使用并推荐的“黄金组合”。
3.1 浏览器开发者工具:你的主战场
现代浏览器(Chrome/Edge/Firefox)的开发者工具是前端审计的起点和核心。你需要熟练掌握以下几个面板:
- Sources(源代码):核心中的核心。这里可以看到页面加载的所有JS、CSS、HTML文件。重点关注:
- Page: 按照域名组织的资源。
- Filesystem: 如果你能关联本地源码(或下载了Source Map),这里可以直接编辑和调试。
- Overrides: 本地覆盖功能,可以修改线上JS文件并在本地生效,用于动态测试补丁或验证漏洞。
- Network(网络):所有网络请求的记录器。这是发现API端点的最快方式。
- 过滤XHR/Fetch请求,快速找到API调用。
- 查看请求头、请求体、响应头、响应体。特别注意
Authorization、Cookie、自定义Token头。 - 右键请求,可以Copy as cURL、Copy as Node.js fetch等,方便在Burp Suite或命令行中重放。
- Console(控制台):不仅仅是输出日志的地方。
- 可以直接执行JS代码,与页面上下文交互。例如,输入
window.localStorage查看存储内容,输入document.cookie查看Cookie。 - 可以重写函数,进行Hook。例如,
XMLHttpRequest.prototype.open = function(...args) { console.log('请求URL:', args[0]); return originalOpen.apply(this, args); }可以监控所有Ajax请求。
- 可以直接执行JS代码,与页面上下文交互。例如,输入
- Application(应用):查看存储信息。
- Local Storage / Session Storage: 常用来存Token、用户信息。
- Cookies: 查看HttpOnly、Secure等属性。
- IndexedDB: 可能存储更结构化的应用数据。
3.2 代码处理与搜索工具
从Network面板下载的JS文件往往是压缩(Minify)或混淆(Obfuscate)过的,可读性极差。
- 美化(Pretty Print):浏览器Sources面板里,压缩的JS文件左下角有一个
{}按钮,点击即可格式化代码,这是第一步。 - 反混淆工具:对于简单的混淆(如变量名替换为
a,b,c),格式化后结合上下文可以阅读。对于复杂的商业混淆(如Jscrambler, JShaman),需要专门的工具或技巧,但这在常规Web渗透中不常见。更多时候,我们依赖搜索和模式匹配。 - 全局搜索:在Sources面板格式化后,使用
Ctrl+Shift+F(Chrome)进行全局搜索。这是最强大的功能之一。搜索关键词包括:- API相关:
/api/v1,fetch,axios,ajax,.post,.get,endpoint,url,baseURL。 - 认证相关:
token,auth,login,Authorization,Bearer,JWT,session。 - 敏感信息:
password,key,secret,access_key,password,encrypt,decrypt。 - 配置相关:
config,setting,debug,test,localhost(可能残留测试配置)。 - 错误处理:
catch,console.error,alert(可能泄露信息)。
- API相关:
3.3 代理与抓包工具
浏览器工具适合主动浏览时的审计,而代理工具适合自动化扫描和深度拦截修改。
- Burp Suite / OWASP ZAP: 行业标准。设置浏览器代理后,所有流量经过它们。
- 重放(Repeater): 修改API请求参数进行越权、注入测试。
- 爬虫(Spider): 自动爬取网站链接,但可能爬不到JS动态加载的内容。
- 主动扫描(Active Scan): 对发现的端点进行漏洞扫描,但需注意授权范围。
- 对比(Comparer): 对比登录前后、不同权限用户的响应差异。
- mitmproxy: 命令行代理,适合自动化脚本集成,轻量且灵活。
3.4 浏览器扩展辅助
一些扩展能提供额外视角:
- Wappalyzer / BuiltWith: 快速识别网站使用的技术栈(JS框架、UI库、服务器、分析工具等),帮你快速建立技术背景。
- EditThisCookie: 方便地查看、编辑和删除Cookie。
- Retire.js: 检查页面引用的JS库是否存在已知漏洞。
3.5 本地分析环境(进阶)
对于特别复杂或重要的目标,可以将前端资源完整下载到本地进行分析。
- 下载工具: 使用
wget -mk或HTTrack等工具镜像网站。 - 静态分析: 使用
grep,ripgrep (rg),Visual Studio Code等工具,在本地代码库中进行更强大的全文搜索和模式匹配。 - Node.js环境: 如果目标应用是Node.js构建的,尝试在本地运行,可能暴露出更多开发阶段的配置和路径。
注意事项:工具是辅助,核心还是人的思维。不要陷入“工具依赖症”。最关键的“工具”是你对HTTP协议、JS语言特性、Web安全漏洞原理的理解。浏览器自带的开发者工具,往往能解决80%的问题。
4. 核心审计流程与关键线索挖掘
有了思维和工具,我们就可以进入实战环节。前端JS审计有一个相对固定的流程,我将其总结为“由外到内,由静到动,由点到面”的十二字方针。下面我们一步步拆解。
4.1 第一步:初步侦察与信息收集(由外到内)
在打开Sources面板之前,先进行快速的外部观察。
- 浏览应用功能:像普通用户一样操作一遍,注册、登录、浏览主要功能模块(用户中心、订单、管理后台入口等)。用Network面板记录下所有的请求。
- 识别技术栈:使用Wappalyzer等扩展,快速了解是React、Vue还是Angular,是否使用了jQuery、Bootstrap等。这决定了你后续搜索代码时的关键词(如React的
setState、Vue的this.$router)。 - 分析网络请求:在Network面板中,重点关注:
- 认证流程:登录请求的URL、参数格式、返回的Token存放位置。
- API模式:URL是否有规律(如
/api/<version>/<resource>)?是否使用GraphQL(请求体为{"query":"..."})? - 敏感数据:哪些响应里直接包含了手机号、邮箱、身份证号等PII信息?这些信息是否必要?
- 错误信息:故意输入错误,看服务器返回的错误信息是否详细(如SQL错误、文件路径、堆栈跟踪)。
4.2 第二步:静态代码分析(由静到动)
这是审计的核心阶段,目标是像“考古”一样,从代码中挖掘信息。
- 获取并美化JS代码:在Network面板找到主要的
app.js、chunk-vendors.js等文件,在Sources面板中打开并点击{}美化。 - 搜索关键字符串:使用全局搜索(
Ctrl+Shift+F)。- 硬编码凭证:搜索
password,secret,key,api_key,token。注意可能被赋值给变量,如const apiKey = 'sk_live_xxxx'。 - 接口端点:搜索
/api,/graphql,fetch,axios.create({baseURL:。 - 配置与标志:搜索
debug,isDebug,test,localhost,development。开发人员可能忘记关闭调试模式或切换API地址。 - 权限关键词:搜索
admin,role,permission,isAdmin,if (role ===。 - 第三方服务:搜索
cloudinary,stripe,aws,s3,firebase。可能泄露配置或访问密钥。
- 硬编码凭证:搜索
- 分析路由与状态管理:
- 对于单页应用,前端路由控制着页面访问。搜索
router,route,/dashboard,/admin。可能发现未在菜单中显示的管理路由。 - 查看状态管理(如Vuex、Redux)的初始化状态或Action,里面可能包含用户角色、权限列表等敏感数据模型。
- 对于单页应用,前端路由控制着页面访问。搜索
- 梳理核心业务函数:找到处理关键业务(如登录
handleLogin、支付submitOrder、上传uploadFile)的函数。分析其:- 参数来源:是用户输入、URL参数还是固定值?
- 校验逻辑:对输入做了哪些过滤或校验?是否仅在客户端校验?
- 请求构造:如何组装发送给后端的请求?参数是否可被预测或篡改?
4.3 第三步:动态交互与漏洞验证(由点到面)
静态分析找到的线索,必须通过动态交互来验证其是否构成真正的漏洞。
- 未授权/越权访问测试:
- 找到获取用户列表的API:
GET /api/users。 - 在未登录状态下,直接使用浏览器访问或使用Burp Repeater重放该请求。
- 如果返回401/403,尝试使用低权限用户(如普通用户)的Token去访问高权限接口(如
GET /api/admin/users)。 - 关键技巧:关注前端路由守卫。如果前端通过
router.beforeEach检查权限,但只是隐藏了导航菜单或跳转,那么直接输入管理页面的URL(如https://target.com/#/admin)可能仍然可以访问。此时再结合Network面板,看访问该页面时调用了哪些API,对这些API进行越权测试。
- 找到获取用户列表的API:
- 接口参数篡改测试:
- 找到创建或更新资源的API,如
POST /api/orders,其请求体可能包含{“productId”: 123, “quantity”: 1, “price”: 100}。 - 尝试修改
price为负数或0,看是否能以异常价格下单。 - 尝试修改
productId为其他用户订单的ID,看是否能操作他人数据(水平越权)。 - 在请求URL或参数中寻找ID,如
GET /api/user/123/profile,尝试将123改为124,访问他人资料。
- 找到创建或更新资源的API,如
- 客户端逻辑绕过测试:
- 最常见的是“仅前端校验”。例如,一个表单提交前,JS代码检查
if(amount > 0),如果小于0就弹出警告。你可以直接通过Burp Suite拦截请求,将amount改为-1,然后转发,观察后端是否接受。 - 另一种是“前端计算校验值”。例如,提交订单时,前端计算了一个总价
totalPrice和其MD5值sign一起发送。你可以修改totalPrice,但需要重新计算sign。如果算法也在前端,你就能找到并复现它。
- 最常见的是“仅前端校验”。例如,一个表单提交前,JS代码检查
- 信息泄露深度挖掘:
- 搜索
console.log,alert,debugger语句。开发人员可能遗留了打印敏感对象(如完整用户对象、API响应)的日志。 - 在JS代码中搜索错误信息的定义。例如,
const ErrorMessages = { 1001: ‘数据库连接失败: {host}’ },这可能泄露内部地址。 - 查看JS Map文件(如果存在
.map文件)。Source Map能将压缩代码映射回源码,可能泄露完整的源代码结构、变量名甚至注释。
- 搜索
4.4 第四步:关联分析与报告整理
将找到的各个“点”串联成“面”,评估其风险。
- 组合漏洞:一个前端信息泄露(如API密钥)可能为后续攻击(如直接调用内部API)打开大门。
- 逻辑链还原:通过多个API调用序列,还原一个完整业务流(如“添加商品到购物车 -> 结算 -> 支付”),从中寻找逻辑缺陷。
- 证据固定:对验证成功的漏洞,务必截图(包含浏览器URL、请求响应)、保存请求数据包(cURL命令或Burp文件)。清晰的证据是报告可信度的基础。
这个流程不是线性的,而是循环往复的。动态测试中发现的奇怪现象,可能会驱使你回到静态代码中寻找原因;静态代码中发现的新端点,又需要动态测试去验证。正是在这种互动中,漏洞的面貌逐渐清晰。
5. 高频漏洞场景与实战案例拆解
理论结合实战才能加深理解。下面我分享几个在前端JS审计中非常常见且容易出成果的漏洞场景,并用简化后的代码案例说明。
5.1 场景一:脆弱的客户端权限校验
这是最经典的漏洞。权限判断仅在前端进行,后端完全信任前端传来的身份标识。
漏洞代码示例(Vue.js + Vue Router):
// router.js const routes = [ { path: '/user', component: UserCenter }, { path: '/admin', component: AdminPanel, meta: { requiresAuth: true, requiresAdmin: true } } ]; router.beforeEach((to, from, next) => { const user = store.state.user; // 从Vuex获取用户信息 if (to.meta.requiresAdmin && user.role !== 'admin') { next('/forbidden'); // 前端跳转到无权限页面 } else { next(); } }); // AdminPanel.vue created() { if (this.$store.state.user.role !== 'admin') { return; // 再次前端判断 } this.fetchAdminData(); // 调用/admin/data接口 }, methods: { async fetchAdminData() { const res = await axios.get('/api/admin/data'); this.data = res.data; } }审计与利用过程:
- 静态分析:搜索
requiresAdmin,role,admin等关键词,找到路由守卫和组件内的权限判断逻辑。 - 动态测试:
- 正常登录一个普通用户
userA。 - 直接手动在地址栏输入
https://target.com/#/admin。 - 观察:页面可能空白或跳转到
/forbidden,但此时打开Network面板。 - 发现:尽管页面跳转,但浏览器仍然尝试发送了
GET /api/admin/data请求! - 验证:在Burp Suite中拦截或直接重放这个请求(携带
userA的Token)。 - 结果:后端很可能返回了管理员数据,因为后端只验证了Token有效,并未校验Token对应的用户角色是否为
admin。
- 正常登录一个普通用户
修复建议:所有关键权限校验必须在后端严格执行。前端校验仅用于改善用户体验(如隐藏按钮),绝不能作为安全依据。
5.2 场景二:API端点与参数的信息泄露
前端代码常常包含完整的API路径和参数结构,这为攻击者绘制“攻击地图”提供了便利。
漏洞代码示例:
// api.js const API_CONFIG = { BASE_URL: process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : 'https://api.prod.com', ENDPOINTS: { GET_USER: (id) => `/v1/users/${id}`, // 用户详情 LIST_USERS: `/v1/users`, // 用户列表 (本应仅管理员可用) UPDATE_PROFILE: `/v1/profile`, // 内部调试接口,忘记删除 DEBUG_STATUS: `/internal/debug/status` } }; // userService.js async function getUserDetail(userId) { // 假设这里从URL获取userId,但未做归属校验 const url = API_CONFIG.ENDPOINTS.GET_USER(userId); return await axios.get(url); }审计与利用过程:
- 静态分析:搜索
ENDPOINTS,/v1/,/api/,很快就能发现这个集中的API配置对象。你会看到LIST_USERS和DEBUG_STATUS这种敏感端点。 - 动态测试:
- 直接构造请求:
GET https://api.prod.com/v1/users。如果后端未做权限控制,可能直接返回所有用户列表。 - 测试调试接口:
GET https://api.prod.com/internal/debug/status。该接口可能返回服务器状态、配置信息甚至数据库连接情况。 - 参数篡改:调用
GET_USER接口时,将userId从自己的ID改为他人的ID,测试水平越权。
- 直接构造请求:
修复建议:
- 避免在前端硬编码敏感或内部API路径。
- 后端应对每一个接口进行独立的身份认证和权限授权检查。
- 构建时使用环境变量,并确保开发环境的配置不会打包到生产版本中。
5.3 场景三:隐藏在压缩代码中的硬编码秘密
开发图方便,有时会把密钥、密码直接写在JS里,构建工具可能会把它们一起打包。
漏洞代码示例(压缩后):
// 压缩后的代码片段,可能来自一个第三方服务集成模块 function initAnalytics(){var e="UA-12345678-1";tracker.init(e)} function getUploadToken(){return"Bearer eyJhbG...很长的一个JWT令牌"}审计与利用过程:
- 静态分析:在美化后的代码中,搜索
key=,token=,secret=,password=,Bearer等模式。像上面的eyJhbG是JWT令牌的典型开头。 - 验证与利用:
- 找到的Google Analytics ID (
UA-12345678-1) 可能用于其他关联攻击。 - 找到的
Bearer令牌,直接用于访问上传API:curl -H "Authorization: Bearer eyJhbG..." https://api.target.com/upload。如果该令牌权限过高,可能导致任意文件上传。
- 找到的Google Analytics ID (
- 深度挖掘:这些秘密可能不是明文的。尝试搜索
atob((Base64解码)或简单的自定义加密函数。例如:
你需要分析这个const encKey = '3d2d1a0c'; function decrypt(s){...} // 一个简单的解密函数 const realKey = decrypt('5a7b8c9d');decrypt函数,并在浏览器Console中执行它来获取解密后的真实密钥。
修复建议:绝对不要在前端代码中存储任何真正的秘密(密钥、密码、永久Token)。前端需要的密钥(如第三方SDK的公开密钥)应是无害的。敏感操作必须通过后端代理进行。
5.4 场景四:不安全的GraphQL实现
GraphQL接口 introspection(自省)查询默认开启时,会泄露完整的Schema。
审计与利用过程:
- 发现端点:在Network面板中寻找发送
application/json且请求体类似{"query":"query {...}"}的请求。或者直接尝试访问/graphql,/gql,/graphql-api等常见路径。 - 执行Introspection查询:在GraphQL端点发送以下查询(可以在浏览器Console中用fetch发送,或在Burp Repeater中发送):
或者使用更完整的Introspection查询脚本。如果成功,你会获得整个API的所有查询(Query)、变更(Mutation)类型和字段信息。query { __schema { types { name fields { name type { name } } } } } - 分析Schema:从返回的Schema中,寻找敏感操作,如
deleteUser,createAdmin,allUsers,updatePermissions等。 - 构造恶意查询:
- 信息泄露:尝试执行
{ allUsers { id email phone role } },看是否能未授权获取所有用户数据。 - 批量查询与DoS:GraphQL允许嵌套查询,可能造成递归深度爆炸,导致服务拒绝服务。例如:
{ user(id:1) { friends { friends { friends {...} } } } }。
- 信息泄露:尝试执行
- 绕过速率限制:如果查询被限速,GraphQL允许将多个查询打包成一个请求(Batching),可能用于绕过基于请求次数的限制。
修复建议:在生产环境关闭Introspection功能。对查询复杂度进行限制(深度、数量)。对每一个字段进行授权检查,而不是仅检查整个查询的入口点。
这些场景只是冰山一角,但覆盖了最常见的安全问题。在实际审计中,你需要保持好奇心,对任何不同寻常的代码模式都多问一个“为什么”和“如果”。
6. 高级技巧与自动化探索
当你掌握了基础方法后,可以尝试一些更高效、更深入的技术,将审计工作提升一个档次。
6.1 使用AST进行深度静态分析
对于大型项目,手动搜索效率低下。抽象语法树(AST)可以将JS代码解析成结构化的树,方便程序化分析。
- 工具:可以使用
esprima、acorn等JS解析库,或直接使用jscodeshift、AST Explorer在线工具。 - 分析什么:
- 查找所有函数调用:特别是
fetch、axios、$.ajax的调用点,提取URL参数。 - 追踪变量传播:跟踪一个敏感变量(如
apiKey)在代码中的传递路径,看它最终在哪里被使用或泄露。 - 识别代码模式:自动识别常见的漏洞模式,如
eval()、innerHTML的直接赋值、localStorage存储敏感信息等。
- 查找所有函数调用:特别是
- 简易示例思路:写一个Node.js脚本,用
esprima解析JS文件,遍历AST,找到所有的CallExpression节点,如果其callee.name是fetch或axios,就打印出它的参数节点,从中提取URL。
6.2 动态Hook与函数拦截
在浏览器Console中重写关键函数,可以实时监控、修改应用行为,是动态分析的利器。
- Hook XMLHttpRequest 和 Fetch:
// 保存原始函数 const originalFetch = window.fetch; const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; // Hook fetch window.fetch = function(...args) { console.log('Fetch调用:', args[0], args[1]); // 可以在这里修改请求参数 // args[1].headers['X-New-Header'] = 'hooked'; return originalFetch.apply(this, args); }; // Hook XHR open XMLHttpRequest.prototype.open = function(method, url, ...rest) { console.log(`XHR Open: ${method} ${url}`); return originalOpen.apply(this, [method, url, ...rest]); }; // Hook XHR send XMLHttpRequest.prototype.send = function(body) { console.log('XHR Send Body:', body); return originalSend.apply(this, arguments); }; - Hook 特定函数:如果你在代码中发现了一个名为
getSecureToken()的函数,可以Hook它来查看其返回值。const originalGetSecureToken = window.getSecureToken; window.getSecureToken = function(...args) { const result = originalGetSecureToken.apply(this, args); console.log('getSecureToken 被调用,返回:', result); return result; };
6.3 自动化信息提取脚本
结合爬虫和静态分析,可以半自动化地收集信息。
- 爬取JS文件:使用工具(如
gospider,hakrawler)或自写脚本,爬取目标网站的所有JS文件链接并下载。 - 批量分析与过滤:写一个Python脚本,用正则表达式或简单字符串匹配,从所有JS文件中提取以下信息:
- 所有符合特定模式的URL(如
/api/[a-z]+)。 - 所有看起来像密钥、令牌的字符串(如匹配
/sk_[a-zA-Z0-9]{30,}/或/eyJhbGciOi[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*/用于JWT)。 - 所有
console.log语句及其参数。
- 所有符合特定模式的URL(如
- 结果去重与排序:将提取出的URL、密钥去重,并按照出现频率或关键词(如
admin,delete,internal)进行排序,生成一份初步的“攻击面报告”。
6.4 结合Source Map还原源码
如果网站部署时没有删除.map文件(例如app.js.map),你就可以完全还原出未经压缩的源代码,包括变量名、函数名、注释,审计难度将大大降低。
- 如何发现:在Sources面板的Page标签下,或者Network面板加载的JS文件列表中,留意是否有
.map文件。 - 如何利用:浏览器开发者工具会自动关联.map文件。你也可以使用
source-map等NPM库在本地还原。还原后的代码几乎和开发环境一样清晰。
高级技巧心得:自动化是为了提高效率,但不能完全替代人工分析。AST分析和Hook技术需要一定的编程基础,初期投入学习成本较高,但一旦掌握,在审计复杂应用时能起到事半功倍的效果。对于日常渗透,熟练使用浏览器的搜索和调试功能已经足够强大。
7. 报告撰写与沟通要点
发现漏洞只是成功了一半,清晰、专业地报告漏洞同样重要。一份好的前端JS审计报告,应该能让开发人员快速理解问题所在并着手修复。
7.1 报告核心结构
- 漏洞标题:简洁明了,如“前端权限校验绕过导致未授权访问管理员API”。
- 风险等级:根据CVSS标准或内部规范,评估为高危、中危、低危。
- 漏洞位置:明确指出漏洞所在的JS文件(如
https://www.target.com/static/js/app.abc123.js)及大概行数或函数名。 - 漏洞描述:
- 现象:普通用户A可以访问/操作本应只有管理员才能访问的数据/功能。
- 根本原因:前端路由和组件中对用户角色进行了校验(
user.role !== ‘admin’),但后端接口/api/admin/data在接收到合法Token后,未对Token对应的用户角色进行二次校验,完全信任了前端的状态。
- 复现步骤:提供一步步的操作指南,让开发人员可以自行验证。
- 使用普通用户账号
test@example.com(密码:xxx)登录系统。
- 使用普通用户账号
- 打开浏览器开发者工具(F12),切换到Network面板。
- 在地址栏直接输入
https://www.target.com/#/admin,页面跳转至无权限页。
- 在地址栏直接输入
- 在Network面板中,找到浏览器自动发出的请求
GET /api/admin/data。
- 在Network面板中,找到浏览器自动发出的请求
- 右键该请求,选择“Copy -> Copy as cURL”。
- 在命令行中执行复制的cURL命令,可以成功获取到管理员数据(响应见附录)。
- 请求/响应示例:附上关键的HTTP请求和响应原始数据(可脱敏)。
- 修复建议:
- 短期缓解:在后端
/api/admin/data接口的入口处,增加对请求用户角色的校验,只有role为admin的用户才允许访问。 - 长期加固:建立统一的权限校验中间件,对所有需要权限的接口进行角色和权限的检查。避免在前端进行任何关键的安全逻辑判断。
- 短期缓解:在后端
- 影响范围:评估受影响的功能模块、用户和数据。
7.2 沟通技巧与注意事项
- 用证据说话:截图、数据包、可复现的步骤是你的最强武器。避免使用“我觉得”、“可能”等模糊词汇。
- 理解开发视角:开发人员可能不了解安全。在指出问题时,可以尝试解释漏洞产生的原理(如“这里属于‘信任客户端’的安全反模式”),而不仅仅是说“你这里写错了”。
- 提供可操作的修复方案:不要只说“这里不安全”,要给出具体的代码修改建议或最佳实践指引。例如,建议使用“基于策略的访问控制(PBAC)”或“在路由层和后端控制器层双重校验”。
- 注意授权范围:确保你的测试和报告在授权的范围内进行。前端JS审计很容易触及到未公开的API,测试这些API可能超出授权范围,务必与项目负责人提前沟通清楚界限。
前端JS审计是一门需要耐心、细心和好奇心的手艺。它要求你不仅是一个黑客,还要像一个代码侦探,从纷繁复杂的字符中寻找逻辑的裂痕。当你通过几行不起眼的JS代码,成功打开一个系统的后门时,那种成就感是无与伦比的。希望这篇长文能成为你手中的那把“破局之钥”,在渗透测试的道路上,打开一扇扇新的大门。记住,代码从不撒谎,它只是静静地等待着能读懂它的人。