htmlspecialchars() 不够用,因只转义少数字符且无法防御富文本中的xss;htmlpurifier可定制白名单、剥离js及危险属性,需在thinkphp 6.x中通过composer安装并配置单例服务,禁用默认配置、显式声明允许标签与协议、关闭svg和事件属性。

为什么 htmlspecialchars() 不够用
它只转义少数几个字符(、<code>>、"、'、&),对 <img src="x" onerror="alert(1)"> 这类绕过完全无效。ThinkPHP 自带的 filter 验证规则或 htmlspecialchars 输出过滤,属于“防御性粉刷”,不是内容净化。
真实场景中,富文本编辑器(如 wangEditor、TinyMCE)提交的 HTML,必须剥离 JS、危险属性、非法协议,同时保留 <p></p>、<strong></strong>、<ul></ul> 等排版标签 —— 这正是 HTMLPurifier 的定位。
- 不校验标签嵌套合法性 → 渲染时可能破坏 DOM 结构
- 不处理
style属性中的expression()或url(javascript:...) - 不拦截
data-*属性里藏的恶意 payload(某些老版本 purifier 默认放行)
如何在 ThinkPHP 6.x 中集成 HTMLPurifier
别用 Composer 全局安装,ThinkPHP 项目应走 composer require ezyang/htmlpurifier 到本地 vendor。然后封装一个服务类,避免每次手动 new 实例(开销大、配置难复用)。
关键点:必须禁用 HTMLPurifier_Config::createDefault() 直接调用,它默认允许太多危险行为;要用自定义配置。
- 配置项
HTML.Allowed显式声明白名单,例如'p,b,i,u,ol,ul,li,a[href|title],img[src|alt|width|height]' - 务必开启
Core.RemoveScriptContents和HTML.SafeIframe(若需嵌入视频) - 设置
URI.AllowedSchemes为['http', 'https', 'mailto'],禁掉javascript:和data: - ThinkPHP 的
validate规则里不能直接塞 purifier 实例,应在app\common\service\PurifyService.php中统一提供clean()方法
示例调用:
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
$content = input('post.content');
$purified = app('purify')->clean($content);
为什么不能在控制器里直接调用 HTMLPurifier
因为 Purifier 初始化耗时明显(尤其首次加载大量 DTD 规则),且内部缓存依赖 HTMLPurifier_Bootstrap 单例。控制器每次请求都 new 一次,会重复解析配置、重建缓存,QPS 下降显著。
更严重的是:ThinkPHP 的容器绑定没自动处理 Purifier 的生命周期,手动 new 出来的实例无法共享配置和缓存,导致同一套规则被反复编译。
- 正确做法是通过
app()->bind()在app\common\provider\AppServiceProvider.php中注册单例 - 配置对象必须复用,不能每次
HTMLPurifier_Config::create(...) - 不要把 purifier 实例塞进 model 的
save()钩子 —— 审计日志、导出等非展示场景不该触发净化
常见 XSS 绕过案例与对应配置补丁
以下输入在默认配置下可能逃逸:@#@#@#@#@#@#@#@#@#@0(十六进制编码)、<img src="x" onerror="alert(1)">(大小写混写)、<svg onload="alert(1)"></svg>(SVG 标签)。
这些不是 bug,是配置疏漏。HTMLPurifier 默认不启用全量编码解码,也不默认禁用 SVG —— 必须主动干预。
- 开启
Core.Encoding为'UTF-8'并设置Core.EscapeNonASCIICharacters为true - 将
HTML.AllowedElements显式设为空数组,再用HTML.Allowed白名单控制,避免继承默认允许 - 禁用 SVG:添加
HTML.ForbiddenElements=>['svg', 'math', 'script'] - 过滤
on*事件属性:设置HTML.Attr.EnableID为false,并确保Attr.AllowedFrameTargets未开放_blank外的 target
真正麻烦的从来不是装上扩展,而是确认每一条规则是否在运行时生效。建议用 var_dump($purifier->config->get('HTML.Allowed')) 在调试阶段核对实际加载的配置。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










