php防xss需分场景处理:html内容用htmlspecialchars(ent_quotes|ent_html5,'utf-8');属性值须加引号并转义;js变量用json_encode();富文本用htmlpurifier白名单过滤。

在PHP框架中拼接HTML标签时,直接用字符串连接(如"<div>".$name."</div>")会把用户输入原样输出到页面,浏览器立刻执行其中的<script></script>或onerror=alert(1)——这不是“显示内容”,而是远程执行任意代码。
纯文本插入HTML内容区
这是最常见也最容易出错的场景:用户昵称、评论正文、商品描述等要放进<p><span><div>这类块级标签里。
<p>第一步:调用<code>htmlspecialchars()并传全三个参数
第二步:确保编码声明为UTF-8,且启用ENT_QUOTES | ENT_HTML5标志
第三步:直接echo结果,不加任何额外包装
这一步操作起来很简单,直接把处理后的变量放进模板里就行。但漏掉任一参数就会留洞:【ENT_QUOTES必须显式传入,否则单引号属性值(如value='xxx')可被绕过】;【UTF-8编码必须写死,不能依赖默认,否则多字节截断攻击可绕过】。
用户数据填进HTML属性值
比如生成<img src="xxx" alt="xxx" title="xxx">或<a href="#" data-id="xxx"></a>,属性值必须用引号包裹,且内容必须转义。
方法一:统一走htmlspecialchars(),但必须手动加引号
echo 'title="'.htmlspecialchars($user_title, ENT_QUOTES | ENT_HTML5, 'UTF-8').'"';
方法二:用框架的attr()辅助函数(如Laravel的e()、ThinkPHP的htmlspecialchars()封装)
注意:属性值没引号包裹时,title=xxx onclick=alert(1)会直接触发XSS,htmlspecialchars()对这种无引号场景完全失效。
动态拼接JavaScript变量或内联事件
别用htmlspecialchars(),它在这里彻底无效。浏览器把<script>var name = "<?php echo $input; ?>";</script>里的引号内内容当JS字符串解析,不是HTML。
必须用json_encode()包裹,并外层用双引号
echo 'var userName = '.json_encode($input, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT).';';
这行代码输出的是合法JS字面量,比如"<script>alert(1)</script>"会被转成"\u003Cscript\u003Ealert(1)\u003C\/script\u003E",浏览器只当普通字符串,不会执行。
富文本内容(如编辑器输出)
不能用htmlspecialchars(),它会把所有<p><strong><img></strong></p>都干掉;也不能用strip_tags(),它拦不住<img onerror="alert(1)">或<a href="javascript:alert(1)"></a>。
安装HTMLPurifier库:composer require ezyang/htmlpurifier
配置白名单,只放行安全标签和属性:
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'p,b,i,a[href],img[src|alt],br,ul,ol,li'); // 显式声明允许项
$purifier = new HTMLPurifier($config);
echo $purifier->purify($raw_html);











