PHP防XSS的核心是管好输出而非堵输入:HTML用htmlspecialchars()全参数转义,URL用urlencode()或http_build_query(),JS上下文禁拼接用户数据,富文本用HTMLPurifier白名单过滤,辅以CSP头和HttpOnly Cookie实现纵深防御。

PHP 防止 XSS 的核心不是“堵住输入”,而是“管好输出”——所有用户数据在进入 HTML、JavaScript、URL 或 CSS 等不同上下文前,必须做对应方式的转义或封装。只靠过滤关键词、删 script 标签、或用 strip_tags 一类粗暴手段,早已无法应对现代绕过手法。
HTML 输出必须用 htmlspecialchars() 并带全参数
任何将用户数据插入 HTML 标签内容或属性值的地方(比如 <div><?php echo $name; ?></div> 或 <a href="#" title="<?php echo $title; ?>">),都必须调用:
htmlspecialchars($string, ENT_QUOTES | ENT_HTML5, 'UTF-8')- ENT_QUOTES 确保单引号和双引号都被转义,防止属性值被闭合绕过
- 明确指定 'UTF-8',避免因字符编码不一致导致的解码绕过(如 UTF-7、UTF-8 双字节混淆)
- 不要用
htmlentities()替代,它对中文等多字节字符处理不稳定,且可能过度转义破坏正常显示
JavaScript 上下文要用 json_encode() 封装
当用户数据要嵌入到内联 <script> 或 JS 变量中(例如 var username = "<?php echo $name; ?>";),htmlspecialchars 不够安全,因为引号、反斜杠、换行等仍可能破坏 JS 结构。
- 正确做法是:用
json_encode($string, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP)输出为合法 JSON 字符串 - 再包裹一层引号,如:
var name = <?php echo json_encode($name, JSON_UNESCAPED_UNICODE); ?>; - 绝对避免拼接未处理的用户数据进 JS 字符串或事件处理器(如
onclick="doSomething('<?php echo $input; ?>')")
URL 参数必须用 urlencode() 编码
用户输入若作为 URL 查询参数(如 href="search.php?q=<?php echo $q; ?>"),不能直接输出,否则可能注入 JavaScript 协议或中断 URL 结构。
立即学习“PHP免费学习笔记(深入)”;
- 一律使用
urlencode($q),确保空格变 %20、& 变 %26、/ 变 %2F 等 - 如果整个 URL 是动态构造的,建议先用
http_build_query()组装参数,它内部已调用 urlencode - 禁止把用户输入直接拼进
javascript:...、vbscript:...、data:text/html,...这类危险协议
富文本需专用过滤器,不能靠简单转义
对于允许用户发帖、编辑器内容、评论含格式等场景,htmlspecialchars 会把所有标签干掉,失去功能;而放任不管又极危险。
- 推荐使用成熟库如 HTMLPurifier,它基于白名单策略,可精确控制哪些标签、属性、CSS、URL 协议允许存在
- 配置时禁用
script、on*事件、style中的 expression/js 表达式、危险协议(javascript:、vbscript:) - 不要自己写正则去“过滤 script 标签”——大小写混淆(
sCrIpT)、注释绕过(<!--><script>)、Unicode 编码等都能轻松绕过
加一层 Content-Security-Policy 响应头
CSP 是纵深防御的关键一环,即使某处输出遗漏转义,也能大幅限制恶意脚本执行能力。
- 在 PHP 响应开头添加:
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'none'; base-uri 'self';"); - 生产环境应移除
'unsafe-inline'和'unsafe-eval',改用 nonce 或 hash 方式加载内联脚本 - 配合
HttpOnlyCookie(session_set_cookie_params(['httponly' => true]);),阻止 XSS 脚本读取 session ID



















