在ThinkPHP 5.1+模板中输出变量时,应优先使用内置过滤器{:$name|html}(TP6)或{:$name|htmlspecialchars}(TP5.1),其底层调用htmlspecialchars($str, ENT_QUOTES, 'UTF-8'),仅转义< > " ' &五个关键字符,兼顾XSS防护与中文兼容性,比htmlentities更轻量、不易乱码。

ThinkPHP 5.1+ 中 html 标签过滤用 htmlspecialchars 还是 htmlentities
直接说结论:在 ThinkPHP 模板中输出变量时,{$name} 默认不转义,必须显式用 {:htmlspecialchars($name)} 或开启模板自动转义;但更推荐用内置的 {:$name|htmlspecialchars} 过滤器,它底层调用的是 htmlspecialchars($str, ENT_QUOTES, 'UTF-8'),能正确处理中文和单双引号,比 htmlentities 更轻量、兼容性更好。
常见错误是直接写 {:$name|htmlentities} —— 它会把所有可表示的 Unicode 字符都转成实体(比如中文“你好”变成 你好),导致页面显示乱码或 SEO 不友好。
-
htmlspecialchars只转义<>"'&这 5 个关键字符,够防 XSS 且不影响可读性 - ThinkPHP 6 默认模板引擎(ThinkTemplate)已内置
html修饰符,等价于htmlspecialchars,写法更简洁:{:$name|html} - 若变量含换行需转
<br>,不能只靠htmlspecialchars,得组合nl2br:{:$content|htmlspecialchars|nl2br}
ThinkPHP 控制器里对用户输入做预过滤:用 filter_var 还是 strip_tags
入库前清理 HTML 标签,别用 strip_tags 简单粗暴删——它无法防御 <img src=x onerror=alert(1)> 这类无闭合标签的 XSS。
真正安全的做法是「白名单过滤」:只保留你需要的标签和属性。ThinkPHP 自带 think\helper\Str::cleanHtml()(TP6.1+),但默认未启用;更通用的是用 filter_var 配合自定义回调,或引入 HTMLPurifier 库。
立即学习“PHP免费学习笔记(深入)”;
- 简单场景(如仅允许
<p><br><strong>):用strip_tags($str, ['p','br','strong'])+ 再过一遍htmlspecialchars - 生产环境富文本(如编辑器内容):必须用
HTMLPurifier,配置白名单:$config->set('HTML.Allowed', 'p,b,i,br,a[href|title]'); - TP5.1 中
input('post.content/s')的/s修饰符只是调用htmlspecialchars,**不删标签**,别误以为它能防 XSS
模板中 {:} 和 {$} 在 HTML 上下文里的行为差异
这是最容易踩坑的地方:你以为写了 {:$name} 就安全,其实它等于原样 echo,浏览器会把其中的 <script> 当代码执行。
{:} 是执行 PHP 表达式,{$} 是输出变量,两者都不自动转义。唯一自动转义的是 {|} 修饰符语法(即 {:$name|html})。
- 错误写法:
<div>{$user_input}</div>→ XSS 风险 - 正确写法:
<div>{$user_input|html}</div>或<div>{:htmlspecialchars($user_input)}</div> - 注意:如果变量已是 HTML 实体(比如数据库里存了
<script></script>),再套一次|html会变成双重编码(显示为),此时应先 <code>html_entity_decode再过滤
全局开启模板自动转义后,哪些地方必须关掉
ThinkPHP 6 支持在 config/template.php 中设置 'default_filter' => 'html',让所有 {$var} 自动过 htmlspecialchars。但这个开关一开,就会影响所有输出——包括你明确需要渲染 HTML 的字段(如后台富文本内容、Markdown 渲染结果)。
- 必须关闭自动转义的场景:
{:raw($html_content)}(TP6)、{:$html_content|raw}(TP5.1)、或直接用<?php echo $html_content; ?> - 别用
{:$html_content|htmlspecialchars|raw}——raw会取消之前所有过滤,等于白干 - 更稳妥的做法:只对用户昵称、简介等纯文本字段开启自动过滤,富文本字段统一走
htmlpurifier处理并标记为可信,输出时用raw
真正麻烦的不是怎么转义,而是分不清「哪里该转」「哪里不该转」「转了几遍」。线上出 XSS,八成是因为某个 {$xxx} 漏了修饰符,或者富文本字段被自动过滤了两次。



















