htmlpurifier是thinkphp 6.x中唯一兼顾安全与可用的富文本净化方案,必须手动严格配置白名单、禁用危险协议与事件属性,并通过服务类统一调用clean()确保全程生效。

富文本场景下,htmlspecialchars() 会把所有标签当普通文本转义,导致页面无法渲染格式 —— 这不是防 XSS,是直接废掉编辑器。真正能兼顾安全与可用的,只有白名单式 HTML 净化,而 ThinkPHP 6.x 中唯一靠谱的选择是 HTMLPurifier,且必须手动配死规则,不能依赖默认配置。
为什么 HTMLPurifier::getInstance() 直接调用就出事
开箱即用的 HTMLPurifier::getInstance() 默认只放行极简标签(比如连 <img> 都被干掉),业务根本没法用;更危险的是,它默认允许 on* 事件、javascript: 协议、data: 协议和内联 style 表达式,等于把门虚掩着等 XSS 蠕虫钻进来。
- 必须显式禁用事件属性:
$config->set('HTML.Attr.EnableID', false)+$config->set('Core.RemoveScriptContents', true) -
URI.AllowedSchemes只留['http', 'https', 'mailto'],javascript:和data:必须从列表里删干净 -
HTML.Allowed别写*或空字符串,按需写死,例如:'p,b,i,u,strong,em,a[href|title|target],img[src|alt|width|height|class]' - 开启
Core.RemoveInvalidTags和Core.EscapeInvalidChildren,否则畸形嵌套(如<script><p></script>)可能绕过解析
ThinkPHP 6 中怎么让 clean() 结果真正生效
在控制器里写 HTMLPurifier::clean($this->request->post('content')) 是典型假动作:它只洗了一次值,但 input('content')、param('content')、验证器、日志、缓存键生成等后续所有地方,读的仍是原始未过滤数据 —— XSS 蠕虫只要找到任意一处漏网输出,就能自我复制传播。
- 必须封装成服务类(如
app\common\service\PurifyService.php),用app('purify')->clean()统一调用,确保配置复用、单例缓存生效 - 不要在中间件里对
$_POST做全局htmlspecialchars(),那会把富文本整个毁掉,JSON 接口也直接 500 - 若需全量参数净化,中间件中应调用
$request->mergeParam()把清洗后的数组合并进 Request 对象,TP6.1+ 才支持;旧版得反射修改$request->param属性 - 验证器里别传原始
input(),先走app('purify')->clean()再校验,否则长度、格式等规则可能误判
白名单配置里最容易被忽略的三个坑
很多团队配完 HTML.Allowed 就以为万事大吉,结果上线后还是被绕过 —— 真正的漏洞往往藏在属性细节和上下文处理里。
-
img[src]不够,得加协议限制:用URI.HostBlacklist拦第三方图床,或自定义 Filter 检查src是否以http://、https://、/开头,否则src="data:image/png;base64,..."可藏 payload -
a[href]允许了,但没关HTML.SafeIframe?那<a href="javascript:alert(1)"></a>仍能执行;若需嵌入视频,必须显式开HTML.SafeIframe并配URI.SafeIframeRegexp -
style属性哪怕只允许color、font-size,若没关CSS.AllowImportant和CSS.Definition->addAllowedProperties,攻击者仍可用expression()或url(javascript:...)触发
白名单不是列几个标签就完事,而是每一条允许的属性都要问一句:它有没有执行能力?能不能带协议?会不会被畸形嵌套利用?XSS 蠕虫靠的就是这些“看起来安全”的缝隙自动扩散。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











