!520 fix(security): 修复 selectpage 中 FIELD 排序 SQL 注入风险

Merge pull request !520 from fix-selectpage-sqli
pull/521/MERGE
Karson 2026-03-23 09:35:29 +00:00 committed by Gitee
commit 43a4451b2c
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
1 changed files with 36 additions and 14 deletions

View File

@ -7,6 +7,7 @@ use think\Config;
use think\Controller;
use think\Hook;
use think\Lang;
use think\Db;
use think\Loader;
use think\Model;
use think\Session;
@ -494,6 +495,18 @@ class Backend extends Controller
$primarykey = $this->request->request("keyField");
//主键值
$primaryvalue = $this->request->request("keyValue");
// keyValue 与 keyField 必须成对合法,否则退回普通搜索,避免出现 [null => ...] 等异常条件
if ($primaryvalue === '') {
$primaryvalue = null;
}
$hasPrimaryInit = false;
if ($primaryvalue !== null) {
if (is_string($primarykey) && preg_match('/^[a-zA-Z0-9_\-]+$/i', $primarykey) === 1) {
$hasPrimaryInit = true;
} else {
$primaryvalue = null;
}
}
//搜索字段
$searchfield = (array)$this->request->request("searchField/a");
//自定义搜索条件
@ -511,8 +524,8 @@ class Backend extends Controller
}
$field = $field ? $field : 'name';
//如果有primaryvalue,说明当前是初始化传值
if ($primaryvalue !== null) {
//如果有 primaryvalue 且 keyField 合法,说明当前是初始化传值
if ($hasPrimaryInit) {
$where = [$primarykey => ['in', $primaryvalue]];
$pagesize = 999999;
} else {
@ -556,17 +569,26 @@ class Backend extends Controller
$fields = is_array($this->selectpageFields) ? $this->selectpageFields : ($this->selectpageFields && $this->selectpageFields != '*' ? explode(',', $this->selectpageFields) : []);
//如果有primaryvalue,说明当前是初始化传值,按照选择顺序排序
if ($primaryvalue !== null && preg_match("/^[a-z0-9_\-]+$/i", $primarykey)) {
$primaryvalue = array_unique(is_array($primaryvalue) ? $primaryvalue : explode(',', $primaryvalue));
//修复自定义data-primary-key为字符串内容时给排序字段添加上引号
$primaryvalue = array_map(function ($value) {
return '\'' . $value . '\'';
}, $primaryvalue);
$primaryvalue = implode(',', $primaryvalue);
$this->model->orderRaw("FIELD(`{$primarykey}`, {$primaryvalue})");
// 初始化传值时按 FIELD 顺序排序(值使用 PDO 转义,防止注入)
if ($hasPrimaryInit && is_string($primarykey) && preg_match('/^[a-zA-Z0-9_\-]+$/i', $primarykey) === 1) {
$primaryValueList = is_array($primaryvalue) ? $primaryvalue : explode(',', (string)$primaryvalue);
$primaryValueList = array_values(array_unique(array_filter(
$primaryValueList,
static function ($v) {
return $v !== null && $v !== '';
}
)));
if ($primaryValueList !== []) {
$pdo = Db::connect()->getPdo();
$quoted = [];
foreach ($primaryValueList as $value) {
$quoted[] = $pdo->quote((string)$value);
}
$columnExpr = '`' . str_replace('.', '`.`', $primarykey) . '`';
$this->model->orderRaw('FIELD(' . $columnExpr . ', ' . implode(',', $quoted) . ')');
} else {
$this->model->order($order);
}
} else {
$this->model->order($order);
}
@ -592,7 +614,7 @@ class Backend extends Controller
}, $result);
$list[] = $result;
}
if ($istree && !$primaryvalue) {
if ($istree && !$hasPrimaryInit) {
$tree = Tree::instance();
$tree->init(collection($list)->toArray(), 'pid');
$list = $tree->getTreeList($tree->getTreeArray(0), $field);