Detailed analysis of an SQL injection vulnerability in an earlier version of the ThinkPHP framework
- 2021-07-07 06:34:31
- OfStack
There was an announcement on ThinkPHP official website that there was an SQL injection vulnerability in ThinkPHP 3.1. 3 and before, and the vulnerability existed in ThinkPHP/Lib/Core/Model. class. php file
Explanation of the method of "preventing SQL injection" according to official documents (refer to http://doc.thinkphp.cn/manual/sql_injection. html)
Using query condition preprocessing prevents SQL injection, and yes, it works 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, it has no effect of "preventing SQL injection" (but the official document says 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 primitive function is:
protected function parseSql($sql,$parse) {
// Parse expression
if(true === $parse) {
$options = $this->_parseOptions();
$sql = $this->db->parseSql($sql,$options);
}elseif(is_array($parse)){ // SQL Pretreatment
$sql = vsprintf($sql,$parse);
}else{
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
}
$this->db->setModel($this->name);
return $sql;
}
Validation 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;
Results:
Table peipeidui All data is listed, and the SQL injection statement takes effect.
Solution:
You can modify the parseSql function to:
protected function parseSql($sql,$parse) {
// Parse expression
if(true === $parse) {
$options = $this->_parseOptions();
$sql = $this->db->parseSql($sql,$options);
}elseif(is_array($parse)){ // SQL Pretreatment
$parse = array_map(array($this->db,'escapeString'),$parse);// This behavior adds a new code
$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 the underlying SQL filtering of TP, programmers should do a good job of security check
2. It is not recommended to use $_ GET and $_ POST directly