htmlspecialchars绕过

焦点 2026-10-17 04:16:36 4618
htmlspecialchars绕过

htmlspecialchars() 是绕过 PHP 开发者最信任的函数之一。它把 &、绕过"、绕过'、绕过<、绕过> 转成实体,绕过理论上能掐死所有反射型 XSS。绕过可现实是绕过,我见过三个生产环境因为它的绕过“默认参数”被打穿。问题从来不在函数本身,绕过而在调用方式。绕过

第一个坑是绕过字符集。老代码经常这么写:htmlspecialchars($input,绕过 ENT_QUOTES)——第二个参数给了,第三个参数没给。绕过PHP 5.6 之前默认字符集是绕过 ISO-8859-1,之后是 UTF-8。但如果你连接的数据库返回 GBK 编码,且页面声明也是 GBK,那转义就形同虚设。攻击者提交 %bf%27 这种宽字节序列,htmlspecialchars 只认 ASCII 字符,%bf 不是它管辖范围,' 被转成 ',但前面的 %bf 和 %5c 在 GBK 里拼成一个合法汉字,反斜杠消失,单引号就“复活”了。这不是理论,是 2006 年就被反复利用的老漏洞,但直到今天还有人踩。

第二个坑更隐蔽——双重编码。你调用了 htmlspecialchars($str, ENT_QUOTES, 'UTF-8'),安全了。但如果这个字符串之前已经经过一次 urldecode 或者 json_decode,而中间层又做了一次 htmlspecialchars,那攻击者可以构造出 &quot; 这种实体。浏览器解析时,先解码 & 为 &,再看到 " 又变成引号。最终渲染结果里,引号还是引号,但你的过滤层已经放行了。我见过一个 CMS,文章标题存数据库前转一次,输出时模板又转一次,结果用户提交 &lt;script&gt;,两次转义后变成 <script>,浏览器照样执行。

第三个坑,也是最容易被忽略的——属性上下文。很多人知道 htmlspecialchars 不处理单引号除非你显式加 ENT_QUOTES,但处理后的输出放在 <a href="..."> 里就安全吗?不。如果你的代码是 href="",攻击者提交 javascript:alert(1)——注意,这里没有引号、尖括号、& 符号,htmlspecialchars 一个字符都不会动。浏览器看到 href="javascript:alert(1)",点击就执行。转义函数管的是特殊字符,但 URL 协议头不在它的职责范围。你需要额外校验 http 或 https 开头,或者用 filter_var($url, FILTER_VALIDATE_URL)。

还有个小众场景:ENT_SUBSTITUTE 和 ENT_DISALLOWED 这两个标志位是 PHP 5.4 加的。默认不启用它们,意味着无效 UTF-8 字节序列会被原样保留。如果你页面是 UTF-8,但攻击者塞入一个畸形字节 \xC0\xBC,某些旧浏览器会按本地字符集(比如 Windows-1252)解析,把 \xC0 当 À,\xBC 当 ¼——都不影响 XSS 载荷构造。真正危险的是,你后端转义了,但浏览器在解析 HTML 时先把字节流做了一次编码嗅探,绕过你的实体转换。

别误会,我不是说 htmlspecialchars 没用。它依然是输出编码的第一道防线,但防线不等于保险箱。正确姿势是:永远显式传三个参数(字符串、ENT_QUOTES|ENT_SUBSTITUTE、'UTF-8'),并且对进入 HTML 不同上下文的数据分别处理——属性里的 URL 要协议白名单,script 标签内容里根本不该用这个函数,应该用 json_encode 配合 ENT_NOQUOTES。更关键的是,所有输入验证应该基于业务逻辑,而不是依赖转义函数兜底。

我上次审计一个支付系统回调,开发者把订单号用 htmlspecialchars 过滤后拼进日志文件。日志文件是纯文本,转义毫无意义,但攻击者通过订单号注入换行符 %0a,直接伪造了后续日志条目。你看,函数本身没错,用错地方就是漏洞。安全编码从来不是记住某个函数,而是理解数据流向的每一站。

本文地址:http://www.79wt.cn/news/9e899982.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

友情链接