Thinkphp3个版本数据库操作以及底层代码分析
字数 1632 2025-08-25 22:58:28
ThinkPHP 3个版本数据库操作及底层代码安全分析
前言
本文详细分析ThinkPHP 3.2.3、5.0.24和6.0开发版三个版本的数据库操作机制及其底层实现,重点探讨SQL注入防护机制和可能的绕过方式。
ThinkPHP 3.2.3分析
基本配置
开启调试模式:
'SHOW_PAGE_TRACE' => true,
数据库配置:
'DB_TYPE' => 'mysql',
'DB_HOST' => 'localhost',
'DB_NAME' => 'thinkphp',
'DB_USER' => 'root',
'DB_PWD' => '123456',
'DB_PORT' => 3306,
'DB_PREFIX' => 'think_',
'DB_CHARSET'=> 'utf8',
'DB_DEBUG' => TRUE, // 记录SQL日志
基本查询示例
field方法:
$User = M("user");
$User->field('username,age')->where(array('age'=>$age))->find();
where方法:
$User->where(array('age'=>$age))->select();
转义机制分析
当传入age=1'时,系统会自动转义。关键调用栈:
find()解析optionsparseSql()依次执行解析函数parseWhere()处理WHERE条件parseWhereItem()处理单个条件项parseValue()最终调用escapeString()进行转义
注入点分析
在parseWhereItem()函数中,存在几个关键判断分支:
if(is_array($val)) {
if(is_string($val[0])) {
$exp = strtolower($val[0]);
if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)) {
// 比较运算,调用parseValue
} elseif(preg_match('/^(notlike|like)$/',$exp)) {
// 模糊查找,调用parseValue
} elseif('bind' == $exp) {
// 使用表达式
} elseif('exp' == $exp) {
// 使用表达式,不调用parseValue
$whereStr .= $key.' '.$val[1];
}
// ...其他条件
}
}
利用方式:
当不使用I函数时,可以构造:
$age = $_GET['age']; // 直接接收参数
$User->where(array('age'=>$age))->find();
Payload:
age[0]=exp&age[1]==1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))) #
I函数的安全防护
I函数默认使用htmlspecialchars过滤,并调用think_filter函数:
function think_filter(&$value){
if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){
$value .= ' ';
}
}
这会使得exp注入失效。
总结
- 使用I函数接收参数时,注入较难实现
- 直接接收参数时,可通过exp表达式注入
- 关键防护点:
parseValue()和escapeString()的调用
ThinkPHP 5.0.24分析
基本查询
Db::table('think_user')->where('id',1)->find();
底层过滤机制
select()方法生成预编译SQL- 使用PDO的
quote()方法转义特殊字符 - 最终生成带占位符的SQL语句
insert方法分析
Db::name('user')->insert(array('username'=>$username));
在parseData()中处理数据:
switch (strtolower($val[0])) {
case 'inc':
$result[$item] = $item . '+' . floatval($val[1]);
break;
case 'dec':
$result[$item] = $item . '-' . floatval($val[1]);
break;
case 'exp':
throw new Exception('not support data:[' . $val[0] . ']');
}
inc和dec会对值进行floatval强制转换exp表达式直接抛出异常
ThinkPHP 6.0开发版分析
基本查询
Db::table('think_user')->where(array('age'=>$age))->fetchSql()->find(1);
过滤机制
- 使用
addslashes对单引号进行转义 - 预编译SQL机制与5.0类似
insert方法
Db::table('think_user')->insert(array('age'=>$age));
在parseData()中:
- 取消了exp表达式支持
- 其他处理方式与5.0类似
版本对比总结
| 特性 | 3.2.3 | 5.0.24 | 6.0 |
|---|---|---|---|
| 参数接收 | I函数/直接接收 | 直接接收 | input函数/直接接收 |
| 转义方式 | escapeString | PDO::quote | addslashes |
| exp表达式 | 支持(可注入) | 抛出异常 | 不支持 |
| 预编译 | 无 | 有 | 有 |
| I函数过滤 | htmlspecialchars | 无 | 无 |
安全建议
- 始终使用最新版本的ThinkPHP
- 使用框架提供的参数接收函数(I/input)
- 避免直接拼接用户输入到查询条件
- 开启调试模式检查生成的SQL语句
- 对复杂查询使用预处理语句
参考文档
- ThinkPHP6.0完全开发手册(预览版)
- ThinkPHP5.0完全开发手册
- ThinkPHP3.2.3完全开发手册
- ThinkPHP框架 5.0.x sql注入漏洞分析
- ThinkPHP3.2.3框架实现安全数据库操作分析