ThinkPHP框架安全实现分析(2)

首先将Model类实例化为一个user对象,然后调用user对象中的where函数处理$map,也就是将$map进行一些格式化处理之后赋值给user对象的成员变量$options(如果有其他的连贯操作,也是先赋值给user对象的对应成员变量,而不是直接拼接SQL语句,所以在写连贯操作的时候,无需像拼接SQL语句一样考虑关键字的顺序),接下来调用find函数。

find函数会调用底层的,也就是driver类中的函数——select来获取数据。到了select函数,又是另一个故事了。

select除了要处理curd操作,还要处理pdo绑定,我们这里只关心curd操作,所以在select中调用了buildSelectSql,处理分页信息,并且调用parseSQL按照既定的顺序把SQL语句组装进去。

虽然拼接SQL语句所需要的参数已经全部放在成员变量里了,但是格式不统一,有可能是字符串格式的,有可能是数组格式的,还有可能是TP提供的特殊查询格式,比如:$data['id']=array('gt','100');,所以在拼接之前,还要调用各自的处理函数,进行统一的格式化处理。我选取了parseWhere这个复杂的典型来分析。

关于安全方面的,如果用I函数来获取数据,那么会默认进行htmlspecialchars处理,能有效抵御xss攻击,但是对SQL注入没有多大影响。

在过滤有关SQL注入有关的符号的时候,TP的做法很机智:先是按正常逻辑处理用户的输入,然后在最接近最终的SQL语句的parseWhere、parseHaving等函数中进行安全处理。这样的顺序避免了在处理的过程中出现注入。

当然处理的方法是最普通的addslashes,根据死在沙滩上的前浪们说,推荐使用mysql_real_escape_string来进行过滤,但是这个函数只能在已经连接了数据库的前提下使用。

感觉TP在这个地方可以做一下优化,毕竟走到这一步的都是连接了数据库的。

恩,接下来,分析开始:

先说几个Model对象中的成员变量:

// 主键名称 protected $pk = 'id'; // 字段信息 protected $fields = array(); // 数据信息 protected $data = array(); // 查询表达式参数 protected $options = array(); // 链操作方法列表 protected $methods = array('strict','order','alias','having','group','lock','distinct','auto','filter','validate','result','token','index','force') 接下来分析where函数: public function where($where,$parse=null){ //如果非数组格式,即where('id=%d&name=%s',array($id,$name)),对传递到字符串中的数组调用mysql里的escapeString进行处理 if(!is_null($parse) && is_string($where)) { if(!is_array($parse)){ $parse = func_get_args();array_shift($parse);} $parse = array_map(array($this->db,'escapeString'),$parse); $where = vsprintf($where,$parse); //vsprintf() 函数把格式化字符串写入变量中 }elseif(is_object($where)){ $where = get_object_vars($where); } if(is_string($where) && '' != $where){ $map = array(); $map['_string'] = $where; $where = $map; } //将$where赋值给$this->where if(isset($this->options['where'])){ $this->options['where'] = array_merge($this->options['where'],$where); }else{ $this->options['where'] = $where; } return $this; }

where函数的逻辑很简单,如果是where('id=%d&name=%s',array($id,$name))这种格式,那就对$id,$name变量调用mysql里的escapeString进行处理。escapeString的实质是调用mysql_real_escape_string、addslashes等函数进行处理。

最后将分析之后的数组赋值到Model对象的成员函数——$where中供下一步处理。

再分析find函数:

//model.class.php 行721 版本3.2.3 public function find($options=array()) { if(is_numeric($options) || is_string($options)){ /*如果传递过来的数据是字符串,不是数组*/ $where[$this->getPk()] = $options; $options = array(); $options['where'] = $where; /*提取出查询条件,并赋值*/ } // 根据主键查找记录 $pk = $this->getPk(); if (is_array($options) && (count($options) > 0) && is_array($pk)) { /*构造复合主键查询条件,此处省略*/ } $options['limit'] = 1; // 总是查找一条记录 $options = $this->_parseOptions($options); // 分析表达式 if(isset($options['cache'])){ /*缓存查询,此处省略*/ } $resultSet = $this->db->select($options); if(false === $resultSet){ return false;} if(empty($resultSet)) { return null; } // 查询结果为空 if(is_string($resultSet)){ return $resultSet;} //查询结果为字符串 // 读取数据后的处理,此处省略简写 $this->data = $this->_read_data($resultSet[0]); return $this->data; }

$Pk为主键,$options为表达式参数,本函数的作用就是完善成员变量——options数组,然后调用db层的select函数查询数据,处理后返回数据。

跟进_parseOptions函数:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/3d5b9b34bafaabb81a3c3006c253aa5c.html