tp5 漏洞修复

This commit is contained in:
sugar1569 2018-12-20 18:27:37 +08:00
parent 322b7d463e
commit 23aafa1a17

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -11,7 +11,6 @@
namespace think\db; namespace think\db;
use BadMethodCallException;
use PDO; use PDO;
use think\Exception; use think\Exception;
@ -26,7 +25,7 @@ abstract class Builder
protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME']; protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME'];
// SQL表达式 // SQL表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%'; protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%';
protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
@ -99,8 +98,11 @@ abstract class Builder
$result = []; $result = [];
foreach ($data as $key => $val) { foreach ($data as $key => $val) {
$item = $this->parseKey($key, $options); $item = $this->parseKey($key, $options, true);
if (is_object($val) && method_exists($val, '__toString')) { if ($val instanceof Expression) {
$result[$item] = $val->getValue();
continue;
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入 // 对象数据写入
$val = $val->__toString(); $val = $val->__toString();
} }
@ -110,8 +112,23 @@ abstract class Builder
} }
} elseif (is_null($val)) { } elseif (is_null($val)) {
$result[$item] = 'NULL'; $result[$item] = 'NULL';
} elseif (isset($val[0]) && 'exp' == $val[0]) { } elseif (is_array($val) && !empty($val)) {
$result[$item] = $val[1]; switch (strtolower($val[0])) {
case 'inc':
//$result[$item] = $item . '+' . floatval($val[1]);
if ($key == $val[1]) {
$result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
}
break;
case 'dec':
//$result[$item] = $item . '-' . floatval($val[1]);
if ($key == $val[1]) {
$result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
}
break;
case 'exp':
throw new Exception('not support data:[' . $val[0] . ']');
}
} elseif (is_scalar($val)) { } elseif (is_scalar($val)) {
// 过滤非标量数据 // 过滤非标量数据
if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) { if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
@ -133,7 +150,7 @@ abstract class Builder
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
return $key; return $key;
} }
@ -174,8 +191,10 @@ abstract class Builder
// 支持 'field1'=>'field2' 这样的字段别名定义 // 支持 'field1'=>'field2' 这样的字段别名定义
$array = []; $array = [];
foreach ($fields as $key => $field) { foreach ($fields as $key => $field) {
if (!is_numeric($key)) { if ($field instanceof Expression) {
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options); $array[] = $field->getValue();
} elseif (!is_numeric($key)) {
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options, true);
} else { } else {
$array[] = $this->parseKey($field, $options); $array[] = $this->parseKey($field, $options);
} }
@ -197,9 +216,6 @@ abstract class Builder
$item = []; $item = [];
foreach ((array) $tables as $key => $table) { foreach ((array) $tables as $key => $table) {
if (!is_numeric($key)) { if (!is_numeric($key)) {
if (strpos($key, '@think')) {
$key = strstr($key, '@think', true);
}
$key = $this->parseSqlTable($key); $key = $this->parseSqlTable($key);
$item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table)); $item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table));
} else { } else {
@ -257,7 +273,9 @@ abstract class Builder
foreach ($where as $key => $val) { foreach ($where as $key => $val) {
$str = []; $str = [];
foreach ($val as $field => $value) { foreach ($val as $field => $value) {
if ($value instanceof \Closure) { if ($value instanceof Expression) {
$str[] = ' ' . $key . ' ( ' . $value->getValue() . ' )';
} elseif ($value instanceof \Closure) {
// 使用闭包查询 // 使用闭包查询
$query = new Query($this->connection); $query = new Query($this->connection);
call_user_func_array($value, [ & $query]); call_user_func_array($value, [ & $query]);
@ -298,7 +316,7 @@ abstract class Builder
protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null) protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null)
{ {
// 字段分析 // 字段分析
$key = $field ? $this->parseKey($field, $options) : ''; $key = $field ? $this->parseKey($field, $options, true) : '';
// 查询规则和条件 // 查询规则和条件
if (!is_array($val)) { if (!is_array($val)) {
@ -331,13 +349,15 @@ abstract class Builder
throw new Exception('where express error:' . $exp); throw new Exception('where express error:' . $exp);
} }
} }
$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field); $bindName = $bindName ?: 'where_' . $rule . '_' . str_replace(['.', '-'], '_', $field);
if (preg_match('/\W/', $bindName)) { if (preg_match('/\W/', $bindName)) {
// 处理带非单词字符的字段名 // 处理带非单词字符的字段名
$bindName = md5($bindName); $bindName = md5($bindName);
} }
if (is_object($value) && method_exists($value, '__toString')) { if ($value instanceof Expression) {
} elseif (is_object($value) && method_exists($value, '__toString')) {
// 对象数据写入 // 对象数据写入
$value = $value->__toString(); $value = $value->__toString();
} }
@ -374,7 +394,11 @@ abstract class Builder
} }
} elseif ('EXP' == $exp) { } elseif ('EXP' == $exp) {
// 表达式查询 // 表达式查询
$whereStr .= '( ' . $key . ' ' . $value . ' )'; if ($value instanceof Expression) {
$whereStr .= '( ' . $key . ' ' . $value->getValue() . ' )';
} else {
throw new Exception('where express error:' . $exp);
}
} elseif (in_array($exp, ['NOT NULL', 'NULL'])) { } elseif (in_array($exp, ['NOT NULL', 'NULL'])) {
// NULL 查询 // NULL 查询
$whereStr .= $key . ' IS ' . $exp; $whereStr .= $key . ' IS ' . $exp;
@ -492,6 +516,11 @@ abstract class Builder
} }
} }
$bindName = $bindName ?: $key; $bindName = $bindName ?: $key;
if ($this->query->isBind($bindName)) {
$bindName .= '_' . str_replace('.', '_', uniqid('', true));
}
$this->query->bind($bindName, $value, $bindType); $this->query->bind($bindName, $value, $bindType);
return ':' . $bindName; return ':' . $bindName;
} }
@ -522,7 +551,9 @@ abstract class Builder
list($table, $type, $on) = $item; list($table, $type, $on) = $item;
$condition = []; $condition = [];
foreach ((array) $on as $val) { foreach ((array) $on as $val) {
if (strpos($val, '=')) { if ($val instanceof Expression) {
$condition[] = $val->getValue();
} elseif (strpos($val, '=')) {
list($val1, $val2) = explode('=', $val, 2); list($val1, $val2) = explode('=', $val, 2);
$condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options);
} else { } else {
@ -546,28 +577,29 @@ abstract class Builder
*/ */
protected function parseOrder($order, $options = []) protected function parseOrder($order, $options = [])
{ {
if (is_array($order)) { if (empty($order)) {
return '';
}
$array = []; $array = [];
foreach ($order as $key => $val) { foreach ($order as $key => $val) {
if (is_numeric($key)) { if ($val instanceof Expression) {
if ('[rand]' == $val) { $array[] = $val->getValue();
if (method_exists($this, 'parseRand')) { } elseif ('[rand]' == $val) {
$array[] = $this->parseRand(); $array[] = $this->parseRand();
} else { } else {
throw new BadMethodCallException('method not exists:' . get_class($this) . '-> parseRand'); if (is_numeric($key)) {
} list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
} elseif (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} else { } else {
$array[] = $val; $sort = $val;
} }
} else { $sort = strtoupper($sort);
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort; $array[] = $this->parseKey($key, $options, true) . $sort;
} }
} }
$order = implode(',', $array); $order = implode(',', $array);
}
return !empty($order) ? ' ORDER BY ' . $order : ''; return !empty($order) ? ' ORDER BY ' . $order : '';
} }
@ -601,6 +633,9 @@ abstract class Builder
*/ */
protected function parseComment($comment) protected function parseComment($comment)
{ {
if (false !== strpos($comment, '*/')) {
$comment = strstr($comment, '*/', true);
}
return !empty($comment) ? ' /* ' . $comment . ' */' : ''; return !empty($comment) ? ' /* ' . $comment . ' */' : '';
} }
@ -630,12 +665,12 @@ abstract class Builder
unset($union['type']); unset($union['type']);
foreach ($union as $u) { foreach ($union as $u) {
if ($u instanceof \Closure) { if ($u instanceof \Closure) {
$sql[] = $type . ' ' . $this->parseClosure($u, false); $sql[] = $type . ' ' . $this->parseClosure($u);
} elseif (is_string($u)) { } elseif (is_string($u)) {
$sql[] = $type . ' ' . $this->parseSqlTable($u); $sql[] = $type . ' ( ' . $this->parseSqlTable($u) . ' )';
} }
} }
return implode(' ', $sql); return ' ' . implode(' ', $sql);
} }
/** /**
@ -650,11 +685,7 @@ abstract class Builder
return ''; return '';
} }
if (is_array($index)) { return sprintf(" FORCE INDEX ( %s ) ", is_array($index) ? implode(',', $index) : $index);
$index = join(",", $index);
}
return sprintf(" FORCE INDEX ( %s ) ", $index);
} }
/** /**
@ -749,7 +780,7 @@ abstract class Builder
$fields = $options['field']; $fields = $options['field'];
} }
foreach ($dataSet as &$data) { foreach ($dataSet as $data) {
foreach ($data as $key => $val) { foreach ($data as $key => $val) {
if (!in_array($key, $fields, true)) { if (!in_array($key, $fields, true)) {
if ($options['strict']) { if ($options['strict']) {
@ -770,19 +801,25 @@ abstract class Builder
} }
$value = array_values($data); $value = array_values($data);
$values[] = 'SELECT ' . implode(',', $value); $values[] = 'SELECT ' . implode(',', $value);
if (!isset($insertFields)) {
$insertFields = array_keys($data);
} }
$fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet))); }
$sql = str_replace(
foreach ($insertFields as $field) {
$fields[] = $this->parseKey($field, $options, true);
}
return str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[ [
$replace ? 'REPLACE' : 'INSERT', $replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options), $this->parseTable($options['table'], $options),
implode(' , ', $fields), implode(' , ', $insertFields),
implode(' UNION ALL ', $values), implode(' UNION ALL ', $values),
$this->parseComment($options['comment']), $this->parseComment($options['comment']),
], $this->insertAllSql); ], $this->insertAllSql);
return $sql;
} }
/** /**