在 PHP 面试中,ThinkPHP 框架的 security 相关问题几乎是中高级岗位的必考内容。很多候选人对“框架已经帮我防了”这句话深信不疑,但一旦面试官追问“底层怎么防的”“什么情况下会失效”,往往就答不上来了。本文围绕 ThinkPHP 中 SQL 注入与 XSS 两大高频漏洞,从原理、框架防护机制、失效场景到面试答题思路,做一次系统梳理。
一、SQL 注入:ThinkPHP 的防线与突破口
1.1 框架层面的防护原理
ThinkPHP 从 3.2 开始,到 5.x、6.x,对 SQL 注入的防护主要依赖三个机制:
- 查询构造器:使用
Db::name('user')->where('id', $id)->find()时,$id会作为参数绑定传入 PDO,而非直接拼接。 - 参数绑定:底层最终调用 PDO 的
prepare+execute,将 SQL 模板与数据分离。 - 自动过滤:对
where数组中的字段名做白名单校验,字段名不符合规则会抛出异常。
面试时如果能说出“ThinkPHP 的 where 方法在解析数组条件时,字段名会经过 parseKey 处理,非法字符会被转义或拦截”,会明显加分。
1.2 常见失效场景
框架不是万能的,以下写法依然可能引入注入:
场景一:原生 SQL 拼接
// 危险写法
$sql = "SELECT * FROM user WHERE id = " . $_GET['id'];
Db::query($sql);
场景二:whereRaw / exp 滥用
// 如果 $name 来自用户输入,且未做绑定
Db::name('user')->whereRaw("name = '$name'")->select();
场景三:字段名可控
// order 字段直接来自参数
Db::name('user')->order($_GET['order'])->select();
ThinkPHP 对 order 的字段名有一定校验,但老版本或特殊写法下仍可能被绕过。
1.3 面试答题模板
“ThinkPHP 默认通过查询构造器和 PDO 参数绑定来防注入。如果面试官问失效场景,我会答三点:一是使用
query或execute直接拼接原生 SQL;二是whereRaw、exp中把变量直接写进字符串;三是字段名、排序字段来自用户输入且未做白名单。正确的做法是始终使用参数绑定,字段名用白名单校验,必要时用bind方法显式绑定。”
二、XSS 防护:ThinkPHP 的转义与模板机制
2.1 XSS 的本质
XSS 的核心是“用户输入被当作 HTML/JS 执行”。防护的关键不是过滤,而是在输出时根据上下文进行转义。
2.2 ThinkPHP 模板引擎的默认行为
ThinkPHP 内置的 Think 模板引擎,默认对 {$var} 输出做 htmlspecialchars 转义。这一点和 Laravel Blade 的 {{ }} 类似。
<!-- 默认转义 -->
{$user.name}
<!-- 不转义,危险 -->
{$user.name|raw}
面试中要能说清楚:默认是安全的,危险的是显式使用 raw 或 |raw 关闭转义。
2.3 常见误区
- 误区一:用了框架模板就万事大吉。如果项目混用了原生 PHP 输出
<?php echo $name; ?>,转义就失效了。 - 误区二:只在输入时过滤。输入过滤无法覆盖所有上下文,存储型 XSS 依然会发生。
- 误区三:富文本场景直接 raw。富文本需要 HTML 白名单过滤,而不是简单转义或完全放行。
2.4 面试答题模板
“ThinkPHP 的模板引擎默认对变量输出做 HTML 转义,所以正常使用
{$var}是安全的。XSS 防护的关键在输出层,而不是输入层。如果面试官问富文本怎么办,我会答:使用 HTMLPurifier 这类库做白名单过滤,而不是用raw直接输出。另外要提醒团队避免混用原生 echo 输出。”
三、高频追问与加分点
3.1 参数绑定为什么能防注入?
因为 SQL 语句的结构在 prepare 阶段就已经确定,数据在 execute 阶段才传入,数据库不会把数据当作 SQL 语法解析。这是从协议层面隔离的,比字符串转义更可靠。
3.2 I() 方法和 input() 方法能防注入吗?
不能。它们只是取参数,可能带一些默认过滤(如 htmlspecialchars),但过滤不等于防注入。防注入的核心是参数绑定。
3.3 如何从代码规范上避免这两类漏洞?
- 禁止在业务代码中直接拼接 SQL,统一走查询构造器。
- 模板中禁止随意使用
raw,需要时走 code review。 - 排序、字段名等“结构化参数”必须白名单校验。
- 引入静态扫描工具,如 PHPStan、SonarQube,配合自定义规则。
四、总结
ThinkPHP 在 SQL 注入和 XSS 上提供了不错的默认防护,但面试官真正想考察的是:你是否理解防护的边界在哪里。能说清楚“框架防了什么、没防什么、什么写法会绕过”,比背一句“用查询构造器就安全了”要有说服力得多。
准备面试时,建议结合自己项目中的真实案例,比如“我们曾经在导出功能里用了 whereRaw 导致注入,后来改成 bind 绑定”,这样的回答既专业又可信。
未经允许不得转载:任鹏个人博客 » ThinkPHP 面试精讲:常见 SQL 注入与 XSS 防护

