前端安全问题的排查,不能只依赖安全测试报告。很多高危漏洞,其实就藏在肉眼可见的HTML、JavaScript和浏览器网络请求里。直接打开浏览器的开发者工具,从页面渲染、资源加载到数据交互,逐层检查,就能发现大量隐患。下面这些方法和步骤,可以直接用到日常的代码审查和线上巡检中。
从DOM结构审查XSS注入点反射型和存储型跨站脚本攻击,最终都要在DOM里落地。排查时,先关注页面中所有会动态渲染用户输入的地方。打开Elements面板,搜索URL参数中出现的字符串、用户昵称、评论内容等。如果这些内容没有被转义就直接插入HTML,就是明显的漏洞。检查时,可以手动在输入框或URL参数中插入测试字符串,比如<span class='xss-test'>test</span>,然后在Elements面板里搜索xss-test这个类名。如果找到了,并且它是以有效HTML标签的形式存在,说明输入没有经过实体化编码。更隐蔽的情况是,用户输入被放进了标签的属性里,却没有用引号闭合。比如<img src={{user_input}}>,攻击者输入x onerror=alert(1)就能执行代码。排查这类问题,要重点检查img、a、iframe等标签的动态属性绑定,以及style属性里的expression注入风险。
内容安全策略是防御XSS的最后一道防线,但配置不当就等于没有。打开Network面板,查看响应头中的Content-Security-Policy字段。常见的致命错误是使用了unsafe-inline和unsafe-eval指令。如果script-src里包含unsafe-inline,内联脚本和事件处理器就能被执行,这让XSS的利用难度大幅降低。另一个高风险配置是default-src设为https:,却允许加载来自任何HTTPS源的内容。攻击者只要能找到任何一个可上传文件的HTTPS站点,就能托管恶意脚本。排查时,还要检查report-uri是否配置了有效的接收端点。如果没有配置报告,或者报告端点本身不可达,安全团队就完全看不到违规事件。更严格的做法是使用nonce或hash机制,而不是直接放行内联脚本。检查时,可以故意触发一个违规请求,看浏览器控制台是否输出CSP报错,以及报告端点是否收到数据。
检查CSRF防护的覆盖范围跨站请求伪造的排查,不能只看有没有Token。打开Elements面板,检查所有form表单是否都携带了CSRF Token,并且Token是放在隐藏字段里,而不是GET参数中。然后切换到Network面板,触发几个状态变更操作,比如修改密码、删除记录、修改邮箱等。查看这些请求是否都是POST、PUT或DELETE方法。如果发现任何敏感操作使用了GET请求,这就是严重的设计缺陷,因为GET请求更容易被img标签或链接发起跨站攻击。还要检查Token的验证逻辑是否严格。可以在控制台手动修改Token的值,或者删除Token字段,重新发送请求,看服务器是否返回了正常的业务响应。如果服务器没有拒绝,说明Token验证存在逻辑漏洞。另外,对于使用SameSite Cookie属性的站点,检查Set-Cookie响应头中是否设置了SameSite=Strict或Lax。如果设置为None却没有Secure标志,浏览器会直接拒绝该Cookie,导致防护失效。
深挖JavaScript依赖链的安全隐患前端项目通常依赖大量第三方库,供应链攻击的入口往往就在这里。打开Sources面板,查看node_modules或vendor目录下的文件。先检查是否有已知的CVE漏洞,使用npm audit或yarn audit生成报告,但不要只看高危漏洞,中低危的prototype pollution或正则表达式拒绝服务,在特定业务场景下同样致命。更隐蔽的风险是,某些库会悄悄读取localStorage或sessionStorage中的数据,并发送到外部域名。排查时,可以在控制台输入以下代码来监控存储操作:
const originalSetItem = localStorage.setItem;
localStorage.setItem = function(key, value) {
console.trace('localStorage.setItem called', key, value);
originalSetItem.apply(this, arguments);
};
运行后,刷新页面或触发相关操作,观察控制台输出。如果发现未知脚本在读取或写入敏感数据,就要追溯来源。还要检查第三方脚本的加载方式。如果通过script标签直接引入CDN上的外部脚本,且没有使用integrity属性进行子资源完整性校验,那么一旦CDN被攻破或脚本被篡改,页面就会直接执行恶意代码。
排查敏感信息在前端的泄露前端代码里经常硬编码API密钥、内部IP地址、测试账号等敏感信息。在Sources面板中,使用全局搜索功能,搜索关键词如key、secret、token、password、api_key、admin。不要只看JS文件,还要检查HTML模板、CSS文件中的注释,以及source map文件。如果项目开启了source map并部署到生产环境,攻击者可以直接还原出未压缩的源代码,从中提取业务逻辑和敏感配置。排查时,打开Network面板,勾选Show source maps选项,如果加载了.map文件,说明存在泄露风险。另外,检查所有AJAX请求的响应体。有些后端接口会返回过多的字段,比如用户列表接口返回了密码哈希、手机号等不该暴露的数据。前端虽然不展示这些字段,但攻击者通过抓包就能获取。
验证CORS配置的边界条件跨域资源共享配置错误,会让恶意站点直接读取API响应。排查时,先看Access-Control-Allow-Origin响应头。如果它的值是*,且请求携带了Cookie,浏览器会直接拦截,这反而是安全的。最危险的情况是,它反射了请求中的Origin头,且没有做严格的白名单校验。可以在控制台用fetch发起一个跨域请求,并修改Origin头为任意域名,看响应头是否原样返回。如果返回了,且Access-Control-Allow-Credentials为true,就存在严重的跨域数据泄露风险。还要检查Access-Control-Allow-Methods是否包含了PUT、DELETE等危险方法。有些站点只允许GET,但错误地开放了所有方法。排查时,发送一个OPTIONS预检请求,查看响应头中的允许方法列表。
检查点击劫持与UI伪装防护点击劫持的排查很简单,检查响应头中是否有X-Frame-Options字段,值是否为DENY或SAMEORIGIN。如果没有这个头,再看是否使用了CSP的frame-ancestors指令。两者都没有的话,页面就可以被嵌入到任意iframe中。攻击者可以通过透明层覆盖,诱导用户点击。排查时,可以自己写一个简单的HTML页面,用iframe嵌入目标页面,看能否正常显示。如果能,就需要添加防护头。另外,还要检查页面中是否使用了sandbox属性不当。有些开发者为了安全给iframe加了sandbox,却同时设置了allow-scripts和allow-same-origin,这两个组合在一起会让sandbox形同虚设,因为脚本可以操作同源资源。
审查WebSocket与长连接的安全性如果页面使用了WebSocket,切换到Network面板的WS过滤器,查看连接建立时的握手请求。检查Origin头是否被服务端正确校验。WebSocket没有同源策略的限制,任何域都可以发起连接,如果服务端不校验Origin,恶意页面就能通过WebSocket收发用户数据。还要检查WebSocket通信的数据格式。如果是明文JSON,且包含敏感操作,攻击者可以在已入侵的页面中注入脚本,通过WebSocket发送伪造指令。排查时,在控制台获取WebSocket实例,尝试发送一些越权数据,看服务端是否返回了预期之外的响应。对于使用SSE的场景,同样要检查EventSource连接时的Cookie和认证信息是否安全。
前端存储与缓存的安全审计打开Application面板,逐项检查localStorage、sessionStorage和IndexedDB中存储的数据。JWT Token、用户个人信息、权限标识等敏感数据,不应该存放在这些可以被JavaScript直接读取的存储中。如果必须存储,要确认Token已经加密,并且设置了较短的过期时间。检查Cookie时,关注HttpOnly、Secure和SameSite属性。登录凭证类的Cookie如果没有设置HttpOnly,一旦发生XSS攻击,Token就会被直接窃取。排查时,在控制台输入document.cookie,如果返回了包含认证信息的Cookie,就说明HttpOnly缺失。还要检查Service Worker的缓存策略。在Application面板的Cache Storage中,查看是否缓存了包含敏感数据的API响应。如果Service Worker缓存了用户详情接口的响应,且没有根据用户身份区分缓存,就可能导致缓存污染,A用户看到B用户的数据。
自动化巡检与持续监控人工排查适合深度分析,但日常巡检需要自动化工具配合。可以在CI/CD流程中集成Lighthouse的自定义审计规则,或者使用Puppeteer编写脚本,在每次构建后自动检查关键安全头、敏感信息泄露和DOM型XSS。下面是一个简单的Puppeteer检查脚本片段,用于检测页面是否包含未转义的用户输入:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com?q=');
const xssExists = await page.evaluate(() => {
return document.querySelector('.xss-test') !== null;
});
if (xssExists) {
console.log('警告:检测到潜在的XSS注入点');
}
await browser.close();
})();
将这类脚本集成到上线流程中,能有效拦截低级但高危的漏洞。同时,利用前端监控系统,实时收集浏览器控制台的CSP违规报告、全局JavaScript错误和资源加载失败事件。当监控到大量CSP违规时,往往意味着正在发生攻击,或者策略配置过于宽松需要收紧。
安全优化从源头开始排查出问题后的修复,不能停留在表面。对于XSS,优先使用框架自带的安全绑定,比如React的JSX会自动转义变量,但dangerouslySetInnerHTML和Vue的v-html指令仍然需要人工审查。对于CSRF,除了Token机制,更推荐采用严格的SameSite Cookie加自定义请求头双重校验。对于第三方依赖,建立私有npm仓库,锁定版本并定期扫描。敏感配置一律通过环境变量注入,不在前端代码中硬编码。CSP策略从严格模式开始部署,先使用Report-Only模式收集一段时间的违规报告,逐步调优后再切换到强制模式。每一次修复后,都要针对同类问题建立自动化测试用例,防止后续迭代中再次引入。
