在 ThinkPHP 8 中,模板变量的 HTML 转义是一个重要的安全特性。以下是详细的转义与关闭转义方法:
1. 默认转义行为
ThinkPHP 8 默认对模板输出的变量进行 HTML 转义,防止 XSS 攻击。
// 控制器
public function index()
{
$data = [
'content' => '<script>alert("xss")</script><b>标题</b>'
];
return view('index', $data);
}
<!-- 模板文件 -->
{$content}
<!-- 输出:<script>alert("xss")</script><b>标题</b> -->
<!-- 所有HTML标签都被转义为实体 -->
2. 关闭转义的方法
方法1:使用 raw 过滤器
{$content|raw}
<!-- 输出原始HTML:<script>alert("xss")</script><b>标题</b> -->
方法2:使用 |raw 简写
{$content|raw}
方法3:在 {php} 标签中使用 html_entity_decode
{php}
echo html_entity_decode($content);
{/php}
3. 批量关闭转义
在控制器中设置不转义:
public function index()
{
$this->filter([]); // 禁用所有过滤器
// 或者指定变量不转义
$content = '<b>标题</b>';
$this->assign([
'content' => $content,
'raw_content' => htmlspecialchars_decode($content)
]);
}
配置全局转义行为:
// config/view.php
return [
// 默认过滤规则
'default_filter' => 'htmlspecialchars',
// 或关闭默认过滤
'default_filter' => '',
];
4. 部分转义场景
转义特定字符:
<!-- 只转义特殊字符 -->
{$content|escape}
使用 nl2br 转换换行:
{$content|nl2br}
<!-- 将换行符转为 <br> 标签 -->
JSON 编码输出:
{$data|json_encode}
5. 自定义过滤函数
// 在控制器中定义过滤函数
public function index()
{
// 自定义安全输出
$safeOutput = function($value) {
// 允许部分安全标签
$allowedTags = '<b><i><u><p><br><span><div>';
return strip_tags($value, $allowedTags);
};
$content = '<b>加粗</b><script>alert("xss")</script>';
$this->assign('content', $safeOutput($content));
}
6. Blade 模板引擎的转义
如果使用 ThinkPHP 8 的 Blade 模板:
<!-- 默认转义 -->
{{ $content }}
<!-- 不转义 -->
{!! $content !!}
<!-- 转义JSON -->
@json($array)
<!-- 在JS中安全输出 -->
<script>
var data = @json($array);
var content = "{{ $content }}"; // 自动转义
</script>
7. 最佳实践建议
安全建议:
- 永远不要信任用户输入
- 默认开启转义
- 只在必要时关闭转义
- 对用户内容进行白名单过滤
示例代码:
// 控制器:对用户输入进行预处理
public function save()
{
$content = input('post.content');
// 1. 清理危险标签
$content = strip_tags($content, '<p><br><b><i><u><a><img>');
// 2. 验证URL
$content = $this->filterUrls($content);
// 3. 分配给视图
$this->assign('content', $content);
}
// 模板:安全输出
{if condition="$isSafe"}
{$content|raw}
{else}
{$content} <!-- 默认转义 -->
{/if}
自定义安全过滤器:
// 创建自定义助手函数
function safe_html($html, $allowedTags = '')
{
if (empty($allowedTags)) {
return htmlspecialchars($html);
}
$html = strip_tags($html, $allowedTags);
// 移除危险属性
$html = preg_replace('/on\w+\s*=/i', '', $html);
$html = preg_replace('/javascript:/i', '', $html);
return $html;
}
// 在模板中使用
{$content|safe_html="<p><a><b><i>"}
8. 常见问题解决
问题1:双重转义
<!-- 错误:已经转义的内容再次转义 -->
{$content|escape|raw} <!-- 错误用法 -->
<!-- 正确:直接输出 -->
{$content}
问题2:富文本编辑器内容
对于富文本编辑器(如UEditor、CKEditor)的内容:
// 1. 入库时只做基本过滤
// 2. 出库时信任内容,但限制允许的标签
// 使用HTMLPurifier等专业库
$purifier = new HTMLPurifier();
$cleanHtml = $purifier->purify($dirtyHtml);
<!-- 模板中直接输出 -->
{$cleanHtml|raw}
总结
- 安全性优先:默认开启HTML转义
- 明确控制:只在明确安全的情况下使用
|raw - 多层防护:结合输入验证、数据处理和输出转义
- 使用专业工具:处理复杂HTML时使用HTMLPurifier等专业库
记住: 永远不要直接输出未经处理的用户输入,即使你认为它是安全的。