识别内联脚本载体需系统扫描:事件属性(onclick/onerror等)、javascript:伪协议(href/src/formaction)、CSS url()或@import、框架危险API(v-html/dangerouslySetInnerHTML)、动态document.write拼接;必须按上下文(HTML/JS/URL/CSS)做敏感编码,CSP需禁用'unsafe-inline'并配合nonce机制。

怎么识别所有内联脚本载体,不只是<script>标签
内嵌脚本漏洞的根源不在<script>标签本身,而在于任何能让用户输入变成可执行代码的上下文。只搜<script>会漏掉90%的风险点。</script>
必须系统性扫描以下位置:
-
onclick、onerror、onload等HTML事件属性(哪怕写在<div>里) -
href="javascript:..."、src="javascript:..."、formaction="javascript:..." - CSS中的
url(javascript:...)或@import语句(尤其在<style>或中) - 现代框架危险API:
v-html(Vue)、dangerouslySetInnerHTML(React)、ng-bind-html(Angular) - 动态拼接的
<script>标签,比如document.write('<script>'+userInput+'</script>')
用正则全局搜索比肉眼快:grep -r -i "on[a-z]\+=\"\|javascript:" ./src/ 或浏览器控制台运行:[...document.querySelectorAll('*')].filter(el => /on\w+=|javascript:/i.test(el.outerHTML))
为什么直接转义HTML不够,必须做上下文敏感编码
很多团队以为对用户输入调用encodeURIComponent()或escapeHtml()就安全了——这是最大误区。不同上下文需要不同编码方式,混用等于没防。
立即学习“前端免费学习笔记(深入)”;
典型错误场景:
- 在
<div onclick="doSomething('<code>userInput')">中,只做HTML实体编码,但'和"仍能闭合字符串 - 在
<script>var data = "<code>userInput";</script>中,用URL编码反而可能绕过过滤 - 在
<a href="data:text/html;base64,...<script>...">中,base64解码后才进入HTML解析流程
正确做法:先确定数据最终落入哪个上下文(HTML body / HTML attribute / JS string / CSS value / URL),再选用对应编码函数。例如JS字符串内插,必须用JSON.stringify()包裹后再插入,而不是escape()。
怎么用开发者工具快速验证DOM型内联脚本是否可触发
静态扫描只能发现“可能有问题”,真正确认漏洞得靠运行时验证。关键不是看有没有弹窗,而是看恶意代码是否被解析执行。
操作步骤:
- 打开浏览器开发者工具 → Sources 面板 → 在可疑JS文件中对
innerHTML、document.write、eval下断点 - 构造测试URL,如
?q=<img src=x onerror=alert(1)>,刷新页面 - 观察Elements面板:目标元素是否真的渲染出
onerror属性?右键→“Edit as HTML”确认未被过滤 - 在Console中手动模拟数据流:
el.innerHTML = '<img src=x onerror=alert(location)>',看是否立即执行 - 检查Network响应:AJAX返回的HTML片段是否含未转义内容,且被前端用
innerHTML直接插入
注意:某些框架(如React)默认不执行内联事件,但若用了dangerouslySetInnerHTML + 后端返回原始HTML,风险立刻回归。
为什么CSP是最后一道防线,但配置错等于没设
CSP不能替代编码,但它能兜底拦截绝大多数内联脚本执行。问题是,很多人配了script-src 'self'却忘了禁用'unsafe-inline',或者误加unsafe-eval。
必须检查响应头或<meta>标签中是否存在以下任一宽松配置:
-
script-src 'unsafe-inline'—— 允许所有onclick、javascript:等 -
script-src 'unsafe-eval'—— 允许eval()、setTimeout("string") -
script-src *或script-src http:—— 完全开放外部脚本 - 缺失
default-src或script-src,导致浏览器降级为宽松策略
推荐最小化配置:Content-Security-Policy: script-src 'self' 'nonce-rAnd0m'; style-src 'self' 'unsafe-inline',并确保每个<script>都带nonce="rAnd0m"。注意:nonce值必须每次请求唯一,不可硬编码。
真正容易被忽略的是,CSP对javascript:伪协议和data: URI的拦截依赖script-src而非default-src,且部分老浏览器不支持frame-ancestors或base-uri指令——这些细节决定防线是否真实有效。



















