分页链接中直接拼接用户输入且未对URL参数调用urlencode()会导致XSS,因paginate()不自动编码、href属URL上下文,需在控制器中对keyword等参数显式urlencode()后再传入url()或分页方法。

分页链接里直接拼接用户输入会触发XSS
ThinkPHP 的 paginate() 方法默认把当前 URL 参数原样塞进分页链接的 href,如果参数含 <script> 或 onerror="alert(1)",且你又用 {$page|raw} 渲染分页 HTML,浏览器就会执行脚本——这不是框架 bug,是你没对 URL 参数做上下文编码。
- 典型错误写法:
<a href="?page=1&keyword=<script>alert(1)</script>">下一页</a> - 真实风险点:搜索关键词、分类 ID、排序字段等通过 GET 传入的参数,可能被恶意构造
-
paginate()本身不处理参数内容,只负责组装 URL 字符串;它信任你传给它的参数是干净的
URL 参数必须用 urlencode(),不是 htmlspecialchars()
分页链接中的查询参数属于 URL 上下文,htmlspecialchars() 对它完全无效——它转义的是 HTML 实体,而 URL 需要的是百分号编码。比如空格变 %20、双引号变 %22、< 变 %3C,否则浏览器解析时仍会识别为 HTML 标签或 JS 事件属性。
- 正确做法:在构造分页前,手动对所有参与 URL 拼接的变量调用
urlencode() - 别信
input('keyword', '', 'htmlspecialchars')—— 这会让keyword在 HTML 内容里安全,但在 URL 里仍是裸的 - ThinkPHP 6 的
url()辅助函数默认不自动编码参数值,需显式处理:url('index/index', ['keyword' => urlencode($keyword)])
模板里渲染分页 HTML 必须禁用 raw,改用 html_entity_decode + 自定义过滤
很多人为了“保留分页样式”直接用 {$page|raw},这等于把整个分页 HTML 字符串当可信内容输出,只要其中某个 href 含未编码参数,就全盘崩塌。更安全的做法是让框架先做基础 HTML 转义,再针对性还原合法标签。
- 禁用
{$page|raw},改用{$page|html_entity_decode|htmlspecialchars}不行——html_entity_decode是解码,不是净化,反而放大风险 - 真正可行的:用
{$page}(默认已转义),然后在控制器中提前对分页对象的 URL 参数做urlencode()处理,确保传给模板的$page里所有href值都已是安全编码后的字符串 - 若用了自定义分页模板(如
think:page标签),检查模板内是否手写了<a href="?<?php echo $param; ?>">—— 这类 PHP 原生输出必须套urlencode()
富文本搜索结果页的分页要额外过 HTMLPurifier
当分页用于展示富文本搜索结果(比如用户可提交带格式的评论),且搜索词被高亮显示在结果中,这时单纯 urlencode() 不够——高亮逻辑可能把原始恶意字符串插入到 <span class="highlight"><script>...</script></span> 里,再经分页链接透出。
立即学习“PHP免费学习笔记(深入)”;
- 关键动作:在搜索逻辑中,对用户输入的
$keyword先过一次HTMLPurifier(哪怕只是白名单允许纯文本标签),再用于高亮和分页参数生成 - 配置示例:
$cfg->set('HTML.Allowed', '');(空白名单 = 只留文本,干掉所有标签) - 别在分页组件里做这个事,应在控制器接收
input('keyword')后立刻处理,保证后续所有使用点拿到的都是净化后值



















