本文实例分析了thinkPHP框架自动填充原理与用法。分享给大家供大家参考,具体如下:
thinkphp有一个自动填充字段的方法
填充规则如下
1 2 3 4 5 | array ( array (完成字段1,完成规则,[完成条件,附加规则]), array (完成字段2,完成规则,[完成条件,附加规则]), ...... ); |
注:研究源码后发现其实还有第4个参数,是给函数或者回调方法传参数用的,参数1默认为该字段值,如:
1 | array ( 'mobile' , 'trim' ,3, 'function' ,参数2,参数3'), |
验证支付动态和静态
静态验证
在模型类里面预先定义好该模型的自动验证规则,在使用create
方法后会自动去验证。
以下是官方的例子
1.首先在模型中定义好验证的规则
1 2 3 4 5 6 7 8 9 10 | namespace Home\Model; use Think\Model; class UserModel extends Model{ protected $_auto = array ( array ( 'status' , '1' ), // 新增的时候把status字段设置为1 array ( 'password' , 'md5' ,3, 'function' ) , // 对password字段在新增和编辑的时候使md5函数处理 array ( 'name' , 'getName' ,3, 'callback' ), // 对name字段在新增和编辑的时候回调getName方法 array ( 'update_time' , 'time' ,2, 'function' ), // 对update_time字段在更新的时候写入当前时间戳 ); } |
2.在调用的时候用create
方法会自动进行填充
1 2 3 4 5 6 7 8 | $User = D( "User" ); // 实例化User对象 if (! $User ->create()){ // 创建数据对象 // 如果创建失败 表示验证没有通过 输出错误提示信息 exit ( $User ->getError()); } else { // 验证通过 写入新增数据 $User ->add(); } |
动态验证
以下是官方的例子
1 2 3 4 5 6 7 8 | $rules = array ( array ( 'status' , '1' ), // 新增的时候把status字段设置为1 array ( 'password' , 'md5' ,3, 'function' ) , // 对password字段在新增和编辑的时候使md5函数处理 array ( 'update_time' , 'time' ,2, 'function' ), // 对update_time字段在更新的时候写入当前时间戳 ); $User = M( 'User' ); $User ->auto( $rules )->create(); $User ->add(); |
下面是核心的代码分析:
create方法调用后会调用autoOperation
方法,方法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | https://www.10zhan.com 版权所有。 /** * 自动表单处理 * @access public * @param array $data 创建数据 * @param string $type 创建类型 * @return mixed */ private function autoOperation(& $data , $type ) { if (! empty ( $this ->options[ 'auto' ])) { $_auto = $this ->options[ 'auto' ]; unset( $this ->options[ 'auto' ]); } elseif (! empty ( $this ->_auto)){ $_auto = $this ->_auto; } // 自动填充 if (isset( $_auto )) { foreach ( $_auto as $auto ){ // 填充因子定义格式 // array('field','填充内容','填充条件','附加规则',[额外参数]) if ( empty ( $auto [2])) $auto [2] = self::MODEL_INSERT; // 默认为新增的时候自动填充 //这里的判断是关键,$type为当前的操作状态,值为1表示是插入,值为2表示是更新 //如果当前的$type状态值等于设置的值$auto[2]或者$auto[2]的值为3,代表需要填充 if ( $type == $auto [2] || $auto [2] == self::MODEL_BOTH) { if ( empty ( $auto [3])) $auto [3] = 'string' ; switch (trim( $auto [3])) { case 'function' : // 使用函数进行填充 字段的值作为参数 case 'callback' : // 使用回调方法 $args = isset( $auto [4])?( array ) $auto [4]: array (); if (isset( $data [ $auto [0]])) { array_unshift ( $args , $data [ $auto [0]]); } if ( 'function' == $auto [3]) { $data [ $auto [0]] = call_user_func_array( $auto [1], $args ); } else { $data [ $auto [0]] = call_user_func_array( array (& $this , $auto [1]), $args ); } break ; case 'field' : // 用其它字段的值进行填充 $data [ $auto [0]] = $data [ $auto [1]]; break ; case 'ignore' : // 为空忽略 if ( $auto [1]=== $data [ $auto [0]]) unset( $data [ $auto [0]]); break ; case 'string' : default : // 默认作为字符串填充 $data [ $auto [0]] = $auto [1]; } if (isset( $data [ $auto [0]]) && false === $data [ $auto [0]] ) unset( $data [ $auto [0]]); } } } return $data ; } |
ThinkPHP官网上曾有一段公告指出,在ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件
根据官方文档对"防止SQL注入"的方法解释(参考https://doc.thinkphp.cn/manual/sql_injection.html)
使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:
1 | $Model ->where( "id=%d and username='%s' and xx='%f'" , array ( $id , $username , $xx ))->select(); |
或者
1 | $Model ->where( "id=%d and username='%s' and xx='%f'" , $id , $username , $xx )->select(); |
但是,当你使用如下代码时,却没有"防止SQL注入"的效果(但是官方文档却说可以防止SQL注入):
1 | $model ->query( 'select * from user where id=%d and status=%s' , $id , $status ); |
或者
1 | $model ->query( 'select * from user where id=%d and status=%s' , array ( $id , $status )); |
原因分析:
ThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.
其原函数为:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 ; } |
验证漏洞(举例):
请求地址:
或
action代码:
1 2 3 | $model =M( 'Peipeidui' ); $m = $model ->query( 'select * from peipeidui where name="%s"' , $_GET [ 'id' ]); dump( $m ); exit ; |
或者:
1 2 3 | $model =M( 'Peipeidui' ); $m = $model ->query( 'select * from peipeidui where name="%s"' , array ( $_GET [ 'id' ])); dump( $m ); exit ; |
结果:
表peipeidui所有数据被列出,SQL注入语句起效.
解决方法:
可将parseSql函数修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 ; } |
总结:
1.不要过分依赖TP的底层SQL过滤,程序员要做好安全检查
2.不建议直接用$_GET,$_POST