There was an announcement on the ThinkPHP official website stating that there is a SQL injection vulnerability in ThinkPHP 3.1.3 and previous versions. The vulnerability exists in the ThinkPHP/Lib/Core/Model.class.php file
According to the official documentation, the method of "preventing SQL injection" is explained (refer to http://doc.thinkphp.cn/manual/sql_injection.html)
Using query condition preprocessing can prevent SQL injection. Yes, it can be effective when using the following code:
$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();
or
$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();
However, when you use the following code, there is no "preventing SQL injection" effect (but the official document says that it can prevent SQL injection):
$model->query('select * from user where id=%d and status=%s',$id,$status);
or
$model->query('select * from user where id=%d and status=%s',array($id,$status));
Cause analysis:
The parseSql function in the ThinkPHP/Lib/Core/Model.class.php file does not implement SQL filtering.
Its original function is:
protected function parseSql($sql,$parse) { // 分析表达式 if(true === $parse) { $options = $this->_parseOptions(); $sql = $this->db->parseSql($sql,$options); }elseif(is_array($parse)){ // SQL预处理 $sql = vsprintf($sql,$parse); }else{ $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX'))); } $this->db->setModel($this->name); return $sql; }
Verification vulnerability (example):
Request address:
http://localhost/Main?id=boo" or 1="1
or
http://localhost/Main?id=boo%22%20or%201=%221
action code:
$model=M('Peipeidui'); $m=$model->query('select * from peipeidui where name="%s"',$_GET['id']); dump($m);exit;
or:
$model=M('Peipeidui'); $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id'])); dump($m);exit;
Result:
All data in table peipeidui is listed, and the SQL injection statement takes effect.
Solution:
The parseSql function can be modified to:
protected function parseSql($sql,$parse) { // 分析表达式 if(true === $parse) { $options = $this->_parseOptions(); $sql = $this->db->parseSql($sql,$options); }elseif(is_array($parse)){ // SQL预处理 $parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码 $sql = vsprintf($sql,$parse); }else{ $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX'))); } $this->db->setModel($this->name); return $sql; }
Summary:
1. Don’t rely too much on TP’s underlying SQL filtering. Programmers must do security checks
2. It is not recommended to use $_GET, $_POST directly