XSS 防御中的 HTML 转义、URL 转义与 JavaScript 转义

跨站脚本攻击(XSS)长期占据 OWASP Top 10 的显著位置,其核心成因是应用程序将不可信数据当作代码执行。防御 XSS 的手段有很多,但最基础、最容易被误解的一环,就是“转义”。很多开发者知道要转义,却分不清 HTML 转义、URL 转义和 JavaScript 转义的适用场景,甚至混用导致防御失效。本文将从 XSS 的常见方式入手,厘清三种转义的本质区别,并给出前端防御的实践建议。

XSS 的常见方式

XSS 通常分为三类:反射型、存储型和 DOM 型。反射型 XSS 发生在服务器将请求参数直接拼接到响应 HTML 中,例如搜索关键词回显;存储型 XSS 则是恶意脚本被持久化到数据库,在帖子、评论、用户资料等位置展示时触发;DOM 型 XSS 不经过服务器,由前端 JavaScript 从 location.hashdocument.referrer 等来源取数据并写入 innerHTML 等危险 sink 导致。

无论哪种类型,攻击者最终目的是让浏览器把数据解释为可执行代码。常见的注入点包括:

  • HTML 标签之间:<div>用户输入</div>,可注入 <script><img onerror>
  • HTML 属性值内:<input value="用户输入">,可闭合引号后添加事件处理器。
  • URL 上下文:<a href="用户输入">,可注入 javascript: 伪协议。
  • JavaScript 代码块内:<script>var name = '用户输入';</script>,可闭合字符串执行任意代码。
  • CSS 或事件属性中:style="用户输入",可利用 expression()url()

正因为注入点上下文不同,单一转义方式无法覆盖所有场景。

三种转义的本质区别

HTML 转义:防御标签与属性注入

HTML 转义的目标是让浏览器把特殊字符当作普通文本,而不是标签或属性边界。需要转义的字符至少包括:

  • &&amp;
  • <&lt;
  • >&gt;
  • "&quot;
  • '&#x27;
  • /&#x2F;(可选,但推荐)

当数据被插入到 HTML 标签之间或属性值中时,必须使用 HTML 转义。例如:

<div>用户输入</div>

若用户输入 <script>alert(1)</script>,转义后变为 &lt;script&gt;alert(1)&lt;&#x2F;script&gt;,浏览器只会显示文本,不会执行脚本。

需要注意的是,HTML 转义不能用于 URL 或 JavaScript 上下文。如果把 javascript:alert(1) 进行 HTML 转义后放入 href,它仍然是可点击的 javascript: 链接。

URL 转义:防御链接与参数注入

URL 转义(又称百分号编码)用于将字符转换为 %XX 形式,确保数据在 URL 中安全传输。它适用于:

  • 将用户输入拼接到 hrefsrc 等 URL 属性中。
  • 构造查询字符串参数。
  • 在 JavaScript 中通过 encodeURIComponent 编码后传给后端。

例如,用户输入 javascript:alert(1),如果直接放入 <a href="用户输入">,点击就会执行脚本。正确的做法是先用 encodeURIComponent 编码,或者更严格地校验协议白名单(只允许 httphttpsmailto 等)。

但 URL 转义不能替代 HTML 转义。如果 URL 本身要放入 HTML 属性,仍需对 &" 等字符做 HTML 转义,否则可能突破属性边界。

JavaScript 转义:防御脚本块内的字符串逃逸

当数据需要嵌入 <script> 标签内的 JavaScript 变量时,必须进行 JavaScript 转义。常见做法是将非字母数字字符转换为 \xHH\uHHHH 形式,尤其是引号、反斜杠、换行符和 </script> 序列。

例如:

<script>
var username = "用户输入";
</script>

如果用户输入 "; alert(1); //,不转义就会直接执行。正确的 JavaScript 转义会将其变为 \x22; alert(1); \x2F\x2F,从而保持在字符串内部。

需要特别提醒:不要用 HTML 转义来处理 JavaScript 上下文,因为 &quot; 在 JavaScript 字符串中不会被解析为引号,反而可能造成逻辑错误;也不要用 URL 转义来处理,因为 %22 在 JavaScript 中只是普通字符序列,不能阻止字符串闭合。

前端防御的实践建议

  1. 根据上下文选择转义方式:HTML 内容用 HTML 转义,URL 属性用 URL 转义,脚本内字符串用 JavaScript 转义。没有一种转义能通吃所有场景。

  2. 优先使用安全的 API:设置文本用 textContent 而非 innerHTML;设置属性用 setAttribute 并配合校验;创建元素用 document.createElement

  3. 对 URL 做协议白名单校验:不要仅依赖转义,应解析 URL 并只允许 httphttps 等安全协议,拒绝 javascript:data: 等危险协议。

  4. 启用 CSP:内容安全策略可以作为纵深防御,限制内联脚本和外部脚本来源,即使转义出现疏漏也能降低危害。

  5. 不要自己写转义函数:使用成熟库,如 DOMPurify 处理富文本,heescape-html 处理 HTML 转义,避免遗漏边界字符。

关联安全话题

XSS 防御只是 Web 安全的一环。与之并列的还有 MySQL 防止 SQL 注入的几种写法,核心是使用预处理语句(Prepared Statement)和参数化查询,避免拼接 SQL 字符串;以及 Linux 服务器安全加固清单,包括最小化安装、及时更新补丁、配置防火墙、禁用 root 远程登录、使用密钥认证、开启审计日志等。三者共同构成从应用层到系统层的防御体系。

小结

HTML 转义、URL 转义和 JavaScript 转义不是可互换的工具,而是针对不同注入上下文的专用手段。理解 XSS 的注入点,才能在正确的场景使用正确的转义,并配合安全 API、协议白名单和 CSP,形成有效的纵深防御。安全无小事,转义用对地方,才是防御的第一步。

未经允许不得转载:任鹏个人博客 » XSS 防御中的 HTML 转义、URL 转义与 JavaScript 转义

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏