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

什么时候该用文本转义而不是 HTML 过滤
纯展示场景下,比如用户昵称、评论内容、日志摘要,只要不渲染为 HTML,就该用转义而非过滤。过滤(如用 DOMPurify)适合富文本,但代价高、规则难维护;而转义是无状态、可预测、零依赖的操作——只要输出到 HTML 上下文,<、>、"、'、& 这五个字符必须处理。
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 默认不转义,必须显式用
<%=(转义)而非<%-(不转义) - 转义只作用于输出位置,对
eval()、setTimeout("...")、内联onerror等动态执行上下文完全无效
对应解法:属性值永远用引号包裹;URL 参数用 encodeURIComponent() 单独处理;v-html 类操作一律加审计标记并走白名单校验。
后端模板(如 Jinja2、EJS)的自动转义陷阱
默认开启 autoescape 并不等于绝对安全:
最易被忽略的是「上下文切换」:同一个变量,在 <script> 里输出时需 JSON 编码 + JSON.parse(),在 CSS 里要用 css.escape()(或至少正则过滤非 ASCII 和括号),不能一股脑套 HTML 转义。

















