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'时,系统会自动转义。关键调用栈:

  1. find()解析options
  2. parseSql()依次执行解析函数
  3. parseWhere()处理WHERE条件
  4. parseWhereItem()处理单个条件项
  5. 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();

底层过滤机制

  1. select()方法生成预编译SQL
  2. 使用PDO的quote()方法转义特殊字符
  3. 最终生成带占位符的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] . ']');
}
  • incdec会对值进行floatval强制转换
  • exp表达式直接抛出异常

ThinkPHP 6.0开发版分析

基本查询

Db::table('think_user')->where(array('age'=>$age))->fetchSql()->find(1);

过滤机制

  1. 使用addslashes对单引号进行转义
  2. 预编译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

安全建议

  1. 始终使用最新版本的ThinkPHP
  2. 使用框架提供的参数接收函数(I/input)
  3. 避免直接拼接用户输入到查询条件
  4. 开启调试模式检查生成的SQL语句
  5. 对复杂查询使用预处理语句

参考文档

  • ThinkPHP6.0完全开发手册(预览版)
  • ThinkPHP5.0完全开发手册
  • ThinkPHP3.2.3完全开发手册
  • ThinkPHP框架 5.0.x sql注入漏洞分析
  • ThinkPHP3.2.3框架实现安全数据库操作分析
ThinkPHP 3个版本数据库操作及底层代码安全分析 前言 本文详细分析ThinkPHP 3.2.3、5.0.24和6.0开发版三个版本的数据库操作机制及其底层实现,重点探讨SQL注入防护机制和可能的绕过方式。 ThinkPHP 3.2.3分析 基本配置 开启调试模式: 数据库配置: 基本查询示例 field方法 : where方法 : 转义机制分析 当传入 age=1' 时,系统会自动转义。关键调用栈: find() 解析options parseSql() 依次执行解析函数 parseWhere() 处理WHERE条件 parseWhereItem() 处理单个条件项 parseValue() 最终调用 escapeString() 进行转义 注入点分析 在 parseWhereItem() 函数中,存在几个关键判断分支: 利用方式 : 当不使用I函数时,可以构造: Payload: I函数的安全防护 I函数默认使用 htmlspecialchars 过滤,并调用 think_filter 函数: 这会使得exp注入失效。 总结 使用I函数接收参数时,注入较难实现 直接接收参数时,可通过exp表达式注入 关键防护点: parseValue() 和 escapeString() 的调用 ThinkPHP 5.0.24分析 基本查询 底层过滤机制 select() 方法生成预编译SQL 使用PDO的 quote() 方法转义特殊字符 最终生成带占位符的SQL语句 insert方法分析 在 parseData() 中处理数据: inc 和 dec 会对值进行 floatval 强制转换 exp 表达式直接抛出异常 ThinkPHP 6.0开发版分析 基本查询 过滤机制 使用 addslashes 对单引号进行转义 预编译SQL机制与5.0类似 insert方法 在 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框架实现安全数据库操作分析