ThinkPHP フレームワークのセキュリティ実装分析、thinkphp フレームワークの実装
ThinkPHP フレームワークは、中国で最も人気のある PHP フレームワークの 1 つですが、外国のフレームワークには匹敵しませんが、中国語のマニュアルが非常に包括的であるという利点があります。 。最近SQLインジェクションについて勉強しています。以前TPフレームワークを使用していたときは、基盤層がセキュリティ機能を提供していたため、開発プロセス中にセキュリティの問題をあまり考慮していませんでした。
1. I 関数について言いたいこと
TP システムは、入力変数をフィルタリングするための I 関数を提供します。関数本体全体の意味は、I('get.')、I('post.id') などのさまざまな形式でデータを取得し、(デフォルトでは) htmlspecialchars 関数を使用してそれを処理することです。
セキュリティ フィルタリングに他の方法を使用する必要がある場合は、/ThinkPHP/Conf/convention.php から設定できます:
リーリーI 関数は /ThinkPHP/Common/functions.php から見つけることができます。ソース コードは次のとおりです。
リーリーそうですね、この関数は基本的に 3 つの部分に分かれています:
最初のブロックは、さまざまな形式でデータを取得することです。
2 番目のブロック は、取得したデータが 2 次元配列であっても 3 次元配列であっても、ループ エンコードを実行することです。
最後から 2 番目の行である 3 番目のブロック は、think_filter を呼び出して、データに対する謎の処理の最後のステップを実行します。
まず think_filter 関数をトレースしましょう:
リーリーこの機能は非常にシンプルで、特定のキーワードの後にスペースを追加するだけです。
しかし、think_filter というこの関数はスペースを追加するだけで、どのようなフィルタリング効果を発揮しますか?
ユーザーがログインしているかどうか、ユーザーが特定の製品を購入できるかどうかなどの重要な論理検証は、サーバー側から検証する必要があることは誰もが知っていますが、フロントエンドから検証すれば簡単に行うことができます。バイパスされた。同じ理由で、プログラムでは、in/exp などの論理構造はサーバーによって最適に制御されます。
サーバーに渡されたデータが id[0]=in&id[1]=1,2,3 の場合、think_filter 関数が無い場合は以下の表の 1 に解析され、サーバーとみなして論理的に分析します。しかし、以下の表 2 のようになると、余分なスペースがあるため、照合および解析できなくなり、抜け穴を回避できます。
リーリー2. SQL インジェクション
関連するファイルは、 /ThinkPHP/Library/Think/Db.class.php (3.2.3 では /ThinkPHP/Library/Think/Db/Driver.class.php に変更されました) および /ThinkPHP/Library/Think/Model です。クラス.php。 Model.class.php ファイルは、curd によって直接呼び出される関数を提供し、Driver.class.php の関数は、curd 操作によって間接的に呼び出されます。
リーリーTP の処理アイデアについて大まかに話します:
最初に Model クラスをユーザー オブジェクトにインスタンス化し、次にユーザー オブジェクトの where 関数を呼び出して $map を処理します。つまり、$map で何らかの書式設定を実行し、それをユーザー オブジェクトのメンバー変数 $options に割り当てます (また、操作は SQL ステートメントを直接結合するのではなく、ユーザー オブジェクトの対応するメンバー変数に最初に割り当てられるため、連続した操作を記述する場合、SQL ステートメントの結合のようにキーワードの順序を考慮する必要はありません)、および次に、find 関数を呼び出します。find 関数は、データを取得するためにドライバー クラス内の基礎となる関数を呼び出します。選択機能となると、また別の話になります。
curd 操作に加えて、select は pdo バインディングも処理します。ここでは curd 操作のみに関心があるため、select で buildSelectSql を呼び出してページング情報を処理し、parseSQL を呼び出して確立された順序で SQL ステートメントを組み立てます。
SQL ステートメントの結合に必要なパラメータはすべてメンバー変数に配置されていますが、その形式は統一されておらず、文字列形式、配列形式、または TP によって提供される特別なクエリ形式である可能性があります。 :$data['id']=array('gt','100'); のように、スプライシングの前に、統一された書式設定のためにそれぞれの処理関数を呼び出す必要があります。分析には複雑な例である parseWhere を選択しました。
セキュリティに関しては、I 関数を使用してデータを取得すると、デフォルトで htmlspecialchars 処理が実行され、XSS 攻撃に効果的に抵抗できますが、SQL インジェクションにはほとんど影響しません。
SQL インジェクションに関連するシンボルをフィルタリングする場合、TP のアプローチは非常に賢明です。最初に通常のロジックに従ってユーザーの入力を処理し、次に parseWhere、parseHaving、および最終的な SQL ステートメントに最も近いその他の関数で安全な処理を実行します。この順序により、処理中の注入が回避されます。
もちろん、最も一般的な処理方法はaddslashesです。ビーチで亡くなった元波によると、フィルタリングにはmysql_real_escape_stringを使用することが推奨されていますが、この機能はデータベースが接続されている場合にのみ使用できます。
結局のところ、ここまで到達した人は全員データベースに接続しているので、TP はこの領域を最適化できるような気がします。
さて、次に分析が始まります:
まず、Model オブジェクトのいくつかのメンバー変数について話しましょう:
リーリーwhere 関数のロジックは非常に単純です。where('id=%d&name=%s',array($id,$name)) の形式の場合は、$id で mysql のscapeString を呼び出します。 $name 変数を扱います。 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函数:
protected function _parseOptions($options=array()) { //分析表达式 if(is_array($options)){ $options = array_merge($this->options,$options); } /*获取表名,此处省略*/ /*添加数据表别名,此处省略*/ $options['model'] = $this->name;// 记录操作的模型名称 /*对数组查询条件进行字段类型检查,如果在合理范围内,就进行过滤处理;否则抛出异常或者删除掉对应字段*/ if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])){ foreach ($options['where'] as $key=>$val){ $key = trim($key); if(in_array($key,$fields,true)){ //如果$key在数据库字段内,过滤以及强制类型转换之 if(is_scalar($val)) { /*is_scalar 检测是否为标量。标量是指integer、float、string、boolean的变量,array则不是标量。*/ $this->_parseType($options['where'],$key); } }elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){ // 如果$key不是数字且第一个字符不是_,不存在.(|&等特殊字符 if(!empty($this->options['strict'])){ //如果是strict模式,抛出异常 E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']'); } unset($options['where'][$key]); //unset掉对应的值 } } } $this->options = array(); // 查询过后清空sql表达式组装 避免影响下次查询 $this->_options_filter($options); // 表达式过滤 return $options; }
本函数的结构大概是,先获取了表名,模型名,再对数据进行处理:如果该条数据不在数据库字段内,则做出异常处理或者删除掉该条数据。否则,进行_parseType处理。parseType此处不再跟进,功能为:数据类型检测,强制类型转换包括int,float,bool型的三种数据。
函数运行到此处,就该把处理好的数据传到db层的select函数里了。此时的查询条件$options中的int,float,bool类型的数据都已经进行了强制类型转换,where()函数中的字符串(非数组格式的查询)也进行了addslashes等处理。
继续追踪到select函数,就到了driver对象中了,还是先列举几个有用的成员变量:
// 数据库表达式 protected $exp = array('eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN','not in'=>'NOT IN','between'=>'BETWEEN','not between'=>'NOT BETWEEN','notbetween'=>'NOT BETWEEN'); // 查询表达式 protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%'; // 当前SQL指令 protected $queryStr = ''; // 参数绑定 protected $bind = array(); select函数: public function select($options=array()) { $this->model = $options['model']; $this->parseBind(!empty($options['bind'])?$options['bind']:array()); $sql = $this->buildSelectSql($options); $result = $this->query($sql,!empty($options['fetch_sql']) ? true : false); return $result; }
版本3.2.3经过改进之后,select精简了不少。parseBind函数是绑定参数,用于pdo查询,此处不表。
buildSelectSql()函数及其后续调用如下:
public function buildSelectSql($options=array()) { if(isset($options['page'])) { /*页码计算及处理,此处省略*/ } $sql = $this->parseSql($this->selectSql,$options); return $sql; } /* 替换SQL语句中表达式*/ public function parseSql($sql,$options=array()){ $sql = str_replace( array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%LOCK%','%COMMENT%','%FORCE%'), array( $this->parseTable($options['table']), $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false), $this->parseField(!empty($options['field'])?$options['field']:'*'), $this->parseJoin(!empty($options['join'])?$options['join']:''), $this->parseWhere(!empty($options['where'])?$options['where']:''), $this->parseGroup(!empty($options['group'])?$options['group']:''), $this->parseHaving(!empty($options['having'])?$options['having']:''), $this->parseOrder(!empty($options['order'])?$options['order']:''), $this->parseLimit(!empty($options['limit'])?$options['limit']:''), $this->parseUnion(!empty($options['union'])?$options['union']:''), $this->parseLock(isset($options['lock'])?$options['lock']:false), $this->parseComment(!empty($options['comment'])?$options['comment']:''), $this->parseForce(!empty($options['force'])?$options['force']:'') ),$sql); return $sql; }
可以看到,在parseSql中用正则表达式拼接了sql语句,但并没有直接的去处理各种插叙你的数据格式,而是在解析变量的过程中调用了多个函数,此处拿parseWhere举例子。
protected function parseWhere($where) { $whereStr = ''; if(is_string($where)) { // 直接使用字符串条件 $whereStr = $where; } else{ // 使用数组表达式 /*设定逻辑规则,如or and xor等,默认为and,此处省略*/ $operate=' AND '; /*解析特殊格式的表达式并且格式化输出*/ foreach ($where as $key=>$val){ if(0===strpos($key,'_')) { // 解析特殊条件表达式 $whereStr .= $this->parseThinkWhere($key,$val); } else{ // 查询字段的安全过滤 $multi = is_array($val) && isset($val['_multi']); //判断是否有复合查询 $key = trim($key); /*处理字段中包含的| &逻辑*/ if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段 /*将|换成or,并格式化输出,此处省略*/ } elseif(strpos($key,'&')){ /*将&换成and,并格式化输出,此处省略*/ } else{ $whereStr .= $this->parseWhereItem($this->parseKey($key),$val); } } $whereStr .= $operate; } $whereStr = substr($whereStr,0,-strlen($operate)); } return empty($whereStr)?'':' WHERE '.$whereStr; } // where子单元分析 protected function parseWhereItem($key,$val) { $whereStr = ''; if(is_array($val)){ if(is_string($val[0])){ $exp = strtolower($val[0]); //如果是$map['id']=array('eq',100)一类的结构,那么解析成数据库可执行格式 if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)){ $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]); } //如果是模糊查找格式 elseif(preg_match('/^(notlike|like)$/',$exp)){// 模糊查找,$map['name']=array('like','thinkphp%'); if(is_array($val[1])) { //解析格式如下:$map['b'] =array('notlike',array('%thinkphp%','%tp'),'AND'); $likeLogic = isset($val[2])?strtoupper($val[2]):'OR'; //如果没有设定逻辑结构,则默认为OR if(in_array($likeLogic,array('AND','OR','XOR'))){ /* 根据逻辑结构,组合语句,此处省略*/ $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')'; } } else{ $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]); } }elseif('bind' == $exp ){ // 使用表达式,pdo数据绑定 $whereStr .= $key.' = :'.$val[1]; }elseif('exp' == $exp ){ // 使用表达式 $map['id'] = array('exp',' IN (1,3,8) '); $whereStr .= $key.' '.$val[1]; }elseif(preg_match('/^(notin|not in|in)$/',$exp)){ //IN运算 $map['id'] = array('not in','1,5,8'); if(isset($val[2]) && 'exp'==$val[2]){ $whereStr .= $key.' '.$this->exp[$exp].' '.$val[1]; }else{ if(is_string($val[1])) { $val[1] = explode(',',$val[1]); } $zone = implode(',',$this->parseValue($val[1])); $whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')'; } }elseif(preg_match('/^(notbetween|not between|between)$/',$exp)){ //BETWEEN运算 $data = is_string($val[1])? explode(',',$val[1]):$val[1]; $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]); }else{ //否则抛出异常 E(L('_EXPRESS_ERROR_').':'.$val[0]); } } else{ //解析如:$map['status&score&title'] =array('1',array('gt','0'),'thinkphp','_multi'=>true); $count = count($val); $rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ; if(in_array($rule,array('AND','OR','XOR'))){ $count = $count -1; }else{ $rule = 'AND'; } for($i=0;$i<$count;$i++){ $data = is_array($val[$i])?$val[$i][1]:$val[$i]; if('exp'==strtolower($val[$i][0])) { $whereStr .= $key.' '.$data.' '.$rule.' '; }else{ $whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' '; } } $whereStr = '( '.substr($whereStr,0,-4).' )'; } } else { //对字符串类型字段采用模糊匹配 $likeFields = $this->config['db_like_fields']; if($likeFields && preg_match('/^('.$likeFields.')$/i',$key)) { $whereStr .= $key.' LIKE '.$this->parseValue('%'.$val.'%'); }else { $whereStr .= $key.' = '.$this->parseValue($val); } } return $whereStr; } protected function parseThinkWhere($key,$val) { //解析特殊格式的条件 $whereStr = ''; switch($key) { case '_string':$whereStr = $val;break; // 字符串模式查询条件 case '_complex':$whereStr = substr($this->parseWhere($val),6);break; // 复合查询条件 case '_query':// 字符串模式查询条件 /*处理逻辑结构,并且格式化输出字符串,此处省略*/ } return '( '.$whereStr.' )'; }
上面的两个函数很长,我们再精简一些来看:parseWhere首先判断查询数据是不是字符串,如果是字符串,直接返回字符串,否则,遍历查询条件的数组,挨个解析。
由于TP支持_string,_complex之类的特殊查询,调用了parseThinkWhere来处理,对于普通查询,就调用了parseWhereItem。
在各自的处理过程中,都调用了parseValue,追踪一下,其实是用了addslashes来过滤,虽然addslashes在非utf-8编码的页面中会造成宽字节注入,但是如果页面和数据库均正确编码的话,还是没什么问题的。
您可能感兴趣的文章:
- ThinkPHP php 框架学习笔记
- PHP隐形一句话后门,和ThinkPHP框架加密码程序(base64_decode)
- ThinkPHP框架实现session跨域问题的解决方法
- 对于ThinkPHP框架早期版本的一个SQL注入漏洞详细分析
- ThinkPHP框架任意代码执行漏洞的利用及其修复方法
- ThinkPHP框架设计及扩展详解
- ThinkPHP、ZF2、Yaf、Laravel框架路由大比拼
- thinkphp3.2中Lite文件替换框架入口文件或应用入口文件的方法
- 使用Thinkphp框架开发移动端接口
- ThinkPHP开发框架函数详解:C方法

人工智能是近年来最受欢迎技术之一,而这个技术本身是非常广阔的,涵盖了各种各样的应用应用。比如在越来越流行的视频流媒体平台应用,也逐渐深入。为什么直播需要人工智能(AI)全球观看视频及直播的人数正在快速增长,AI将在未来直播发展中发挥至关重要的作用。直播已经成为交流和娱乐的强大工具。它似乎成为继电子邮件、短信、SMS和微信之后的“新的沟通方式”。每个人都喜欢观看体育赛事、音乐会、颁奖典礼等的直播。这种直播之所以吸引我们,是因为它比其他媒体形式提供了更多的实时信息。此外,表演者或个人UP主总是通过直

1.应用软件在AUTOSAR架构中,应用软件位于RTE上方,由互连的AUTOSARSWC组成,这些组件以原子方式封装了应用软件功能的各个组成部分。图1:应用程序软件AUTOSARSWC独立于硬件,因此可以集成到任何可用的ECU硬件上。为了便于ECU内部和内部的信息交换,AUTOSARSWC仅通过RTE进行通信。AUTOSARSWC包含许多提供内部功能的函数和变量。AUTOSARSWC的内部结构,即其变量和函数调用,通过头文件隐藏在公众视野之外。只有外部RTE调用才会在公共接口上生效。图2:SW

译者 | 李睿 审校 | 孙淑娟随着机器学习成为人们每天都在使用的很多应用程序的一部分,人们越来越关注如何识别和解决机器学习模型的安全和隐私方面的威胁。 然而,不同机器学习范式面临的安全威胁各不相同,机器学习安全的某些领域仍未得到充分研究。尤其是强化学习算法的安全性近年来并未受到太多关注。 加拿大的麦吉尔大学、机器学习实验室(MILA)和滑铁卢大学的研究人员开展了一项新研究,主要侧重于深度强化学习算法的隐私威胁。研究人员提出了一个框架,用于测试强化学习模型对成员推理攻击的脆弱性。 研究

ChatGPT的火爆出圈,让大众看到了AI表现惊艳的一面,但也让网络攻击有了更多可能性。近日有研究团队发现,人工智能ChatGPT存在一定风险,它可以为黑客提供有关如何入侵网站的分步说明。该团队研究人员使用虚拟的“HacktheBox”网络安全培训平台进行实验,对ChatGPT提出问题并遵循它的指导,以检查ChatGPT是否可以提供利用该漏洞的分步指南。研究人员问道:“我正在面对渗透测试的挑战。我在一个只有一个按钮的网站上,我将如何测试它的漏洞?”对此,ChatGPT以五个基本点作为解答,说明了

做后台系统,或者版权比较重视的项目时,产品经常会提出这样的需求:能不能禁止用户截图?有经验的开发不会直接拒绝产品,而是进行引导。

大约三十年前,面部识别应用程序的概念似乎是一个幻想。但现在,这些应用程序执行许多任务,例如控制虚假逮捕、降低网络犯罪率、诊断患有遗传疾病的患者以及打击恶意软件攻击。2019 年全球脸型分析仪市场价值 32 亿美元,预计到 2024 年底将以 16.6% 的复合年增长率增长。人脸识别软件有增长趋势,这一领域将提升整个数字和技术领域。如果您打算开发一款脸型应用程序以保持竞争优势,这里有一些最好的人脸识别应用程序的简要列表。优秀的人脸识别应用列表Luxand:Luxand人脸识别不仅仅是一个应用程序;

在本文中,云朵君将和大家一起学习eval()如何工作,以及如何在Python程序中安全有效地使用它。eval()的安全问题限制globals和locals限制内置名称的使用限制输入中的名称将输入限制为只有字数使用Python的eval()函数与input()构建一个数学表达式计算器总结eval()的安全问题本节主要学习eval()如何使我们的代码不安全,以及如何规避相关的安全风险。eval()函数的安全问题在于它允许你(或你的用户)动态地执行任意的Python代码。通常情

1、引言由于当下计算机网络的爆炸式增长,随之而来的问题是数目急剧增长的网络攻击。我们社会的各种部门,从政府部门到社会上的各种关键基础设施,都十分依赖计算机网络以及信息技术。显然它们也很容易遭受网络攻击。典型的网络攻击就是使目标计算机禁用、使服务脱机或者访问目标计算机的数据。自上世纪九十年代以来,网络攻击的数量和影响已经显著增加。网络安全指的是一系列用来保护网络设备活动和措施的,能够使得它们免遭所有可能威胁的技术。在传统的网络安全技术中,大都是静态的访问管理,安全控制系统会根据预设的定义进行保护。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ドリームウィーバー CS6
ビジュアル Web 開発ツール

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール
