htmlspecialchars() 不是万能解药,仅适用于 HTML 文本及部分属性上下文;输出到 JS 须用 json_encode() 包裹;富文本必须用白名单解析器如 HTMLPurifier。

PHP 里没有开箱即用的“XSS 过滤函数”,htmlspecialchars() 是最常用、最可靠的第一道防线,但直接套用它就以为安全了,反而最容易出事。
为什么 htmlspecialchars() 不是万能解药
它只对 HTML 实体上下文有效,比如把 <script></script> 变成 <script>。但如果你把用户输入拼进 onclick="..."、href="javascript:..." 或 <style></style> 里,它完全不生效——因为那些地方不是 HTML 文本上下文,而是 JavaScript 或 CSS 上下文。
- 常见错误现象:
htmlspecialchars($user_input)后仍可触发onclick="alert(1)"类攻击 - 使用场景:仅适用于输出到 HTML 元素内容(如
<div>xxx</div>)或属性值中且该属性是纯文本型(如title="xxx") - 参数差异:必须显式传
ENT_QUOTES | ENT_HTML5,并指定UTF-8编码,否则在某些旧环境会绕过 - 示例:
htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8')
输出到 JS 或事件属性时,得用 JSON 编码 + 引号包裹
把用户数据当 JS 字符串用,唯一安全做法是先 json_encode(),再用单/双引号包住,放进 onclick 或内联 script 中。不能靠字符串拼接或正则过滤。
- 常见错误现象:用
str_replace()去掉javascript:,结果被jaVasCript:或编码绕过 - 使用场景:动态生成内联 JS、Vue/React 模板中的插值(服务端渲染时)、
data-属性存复杂结构 - 性能影响:
json_encode()开销极小,比任何正则匹配都快且确定 - 示例:
echo 'onclick="doAction(' . json_encode($user_id, JSON_UNESCAPED_UNICODE) . ')"';
富文本场景不能靠过滤,得用白名单解析器
如果真要允许用户发带格式的内容(比如后台编辑器),strip_tags() 或正则删 <script></script> 标签根本不可靠——HTML 解析器能识别嵌套、注释、大小写混淆、空格变形等所有绕过手法。
立即学习“PHP免费学习笔记(深入)”;
- 常见错误现象:
strip_tags($html, '<p><br><b>')
后仍执行<img src=x onerror=alert(1)> - 使用场景:CMS 后台、论坛发帖、邮件模板渲染
- 推荐方案:用
HTMLPurifier(配置严格白名单)或现代替代品如league/html-sanitizer - 注意:
DOMDocument+ 白名单遍历也可行,但实现成本高、易漏判;别自己写“简单过滤”
真正难的不是选哪个函数,而是每次输出前都得问一句:“这个变量最终会落在哪一层上下文里?”——HTML?JS 字符串?JS 数值?CSS?URL?不同层对应不同编码策略,混用就等于开门。











