PHP防XSS必须按输出上下文严格转义:HTML用htmlspecialchars($str, ENT_QUOTES | ENT_HTML5, 'UTF-8');JS上下文须用json_encode()并外层双引号包裹;禁用无参htmlspecialchars、htmlentities、strip_tags及addslashes。

PHP 防 XSS 不是“加个过滤函数就完事”,而是必须在每次输出时,按 HTML、JS、CSS、属性等不同上下文,选用严格匹配的转义方式。漏掉任一上下文,XSS 就可能触发。
所有 echo 输出到 HTML 文本或属性前必须用 htmlspecialchars() 显式传参
浏览器解析 HTML 时,只要用户数据没被转义就直接进标签内容或双引号属性里,<script> 或 onerror=alert(1) 就会执行。
-
htmlspecialchars($str, ENT_QUOTES | ENT_HTML5, 'UTF-8')是唯一推荐写法:强制转义单双引号、兼容 HTML5 实体(如')、显式声明编码防多字节截断绕过 - 禁用无参调用
htmlspecialchars($str):默认只转双引号,单引号属性(如value='xxx')可被绕过 - 别用
htmlentities():它把中文、emoji 全转成你类实体,破坏可读性且不可逆 - 别信
strip_tags():它不处理<img onerror="...">、注释混淆(<!--<script>-->)或大小写变形(<ScRiPt>)
输出到 <script> 或 JS 属性时,htmlspecialchars() 完全无效
浏览器把 <script>var x = "<?php echo $user; ?>";</script> 里的引号内内容当 JS 字符串解析,不是 HTML。此时 " 或 \ 会被 JS 引擎直接执行,htmlspecialchars() 压根不生效。
- 必须用
json_encode($user, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) - 外层 JS 字符串必须用双引号包裹:
var msg = <?php echo json_encode($user, ...); ?>;(注意:没有额外引号) - 禁用
addslashes()或手写str_replace():JS 解析器不吃这套,"\u0022"这类 Unicode 转义仍可被利用 - 避免在
data-属性里塞未处理的用户数据:<div data-msg="<?php echo $user; ?>">同样危险,应统一走json_encode()
富文本场景不能靠 htmlspecialchars(),必须用 HTMLPurifier 白名单
论坛、商品描述、后台编辑器需要保留 <p>、<img src> 等合法标签,但又要拦住 <script>、onerror、javascript:。这时候 htmlspecialchars() 把所有标签都干掉,strip_tags() 又拦不住变体攻击。
立即学习“PHP免费学习笔记(深入)”;
- 用 Composer 安装
HTMLPurifier,配置只放行必要标签和属性,例如:$config->set('HTML.Allowed', 'p,strong,em,a[href|title],img[src|alt]'); - 强制校验
a[href]协议为http://或https://,禁用javascript:、data: - 禁止
on*事件属性、style中的expression()、url(javascript:...) - 绝对不要手写正则删
<script>:会被<scr>ipt</scr>、空格/换行/注释干扰绕过
CSS 内联样式和 URL 字段需单独校验,不能无脑转义
<div style="color: <?php echo $color; ?>;"> 这种输出点,htmlspecialchars() 只能防引号闭合,防不了 expression(alert(1)) 或 url(javascript:...) 这类 CSS 注入。
- 对颜色值用正则校验:
/^#([0-9A-F]{3}){1,2}$/i,仅通过才输出原始值 - 对 URL 字段用
filter_var($url, FILTER_VALIDATE_URL),再检查parse_url($url, PHP_URL_SCHEME)是否为http或https - 对 class 名、id 名等标识符,只允许字母数字下划线:
/^[a-zA-Z0-9_]+$/ - 别在
style或href里拼接未校验的用户输入——哪怕只差一个字符,也可能打开整个攻击面
最易被忽略的是:同一个变量可能在多个上下文中输出(比如既进 HTML 文本,又进 data- 属性,还进 JS 字符串),每个位置都得单独处理;还有就是数据库查出的老数据,哪怕当年入库时“过滤过”,现在输出时也必须重新按上下文转义——XSS 的触发点永远在输出渲染那一刻,不是入库那一刻。



















