纯展示场景(如用户昵称、评论)应优先用文本转义而非html过滤;转义无状态、零依赖,只需处理&、、"、'五个字符;javascript中应单次遍历映射,避免正则顺序错误导致二次转义。

什么时候该用文本转义而不是 HTML 过滤
纯展示场景下,比如用户昵称、评论内容、日志摘要,只要不渲染为 HTML,就该用转义而非过滤。过滤(如用 DOMPurify)适合富文本,但代价高、规则难维护;而转义是无状态、可预测、零依赖的操作——只要输出到 HTML 上下文,、<code>>、"、'、& 这五个字符必须处理。
JavaScript 中最简安全的转义函数怎么写
别用正则全局替换 再替换 <code>> —— 顺序错会导致 < 被二次转义成 。正确做法是单次遍历、逐字符映射:
function escapeHtml(text) {
const map = {
'&': '&',
'': '>',
'"': '"',
"'": '''
};
return String(text).replace(/[&"']/g, char => map[char]);
}
注意:String() 强制转换避免 undefined 或 null 报错;正则里用 [&"'] 而不是 .,既精准又快;' 比 ' 兼容性更好(IE8 也认)。
模板引擎里漏掉转义的典型位置
很多开发者只记得插值({{ name }}),却忽略这些地方:
-
v-html(Vue)或innerHTML赋值——这根本不是转义能解决的,属于信任边界突破,必须拒绝原始字符串 - 属性值未加引号:
<div data-id="{{" id> → 攻击者让 <code>id变成123 onclick=alert(1),直接执行 - URL 参数拼接:
<a href="https://www.php.cn/link/8e7aec754122752b363723f8ea82b77d">...</a>→name里含" onclick=...会逃逸出属性上下文 - Jinja2 的
{{ value|safe }}会绕过所有转义——检查代码里有没有无意识加了|safe - EJS 默认不转义,必须显式用
(转义)而非 <code>(不转义) - 转义只作用于输出位置,对
eval()、setTimeout("...")、内联onerror等动态执行上下文完全无效
对应解法:属性值永远用引号包裹;URL 参数用 encodeURIComponent() 单独处理;v-html 类操作一律加审计标记并走白名单校验。
后端模板(如 Jinja2、EJS)的自动转义陷阱
默认开启 autoescape 并不等于绝对安全:
最易被忽略的是「上下文切换」:同一个变量,在 <script></script> 里输出时需 JSON 编码 + JSON.parse(),在 CSS 里要用 css.escape()(或至少正则过滤非 ASCII 和括号),不能一股脑套 HTML 转义。










