详解php比较操作符的安全问题


Posted in PHP onDecember 03, 2015

php的比较操作符有==(等于)松散比较,===(完全等于)严格比较,这里面就会引入很多有意思的问题。

在松散比较的时候,php会将他们的类型统一,比如说字符到数字,非bool类型转换成bool类型,为了避免意想不到的运行效果,应该使用严格比较。如下是php manual上的比较运算符表:

例子    名称     结果
$a == $b  等于   TRUE,如果类型转换后 $a 等于 $b。
$a === $b  全等   TRUE,如果 $a 等于 $b,并且它们的类型也相同。
$a != $b  不等   TRUE,如果类型转换后 $a 不等于 $b。
$a <> $b  不等   TRUE,如果类型转换后 $a 不等于 $b。
$a !== $b  不全等   TRUE,如果 $a 不等于 $b,或者它们的类型不同。
$a < $b   小与   TRUE,如果 $a 严格小于 $b。
$a > $b   大于   TRUE,如果 $a 严格大于 $b。
$a <= $b  小于等于   TRUE,如果 $a 小于或者等于 $b。
$a >= $b  大于等于   TRUE,如果 $a 大于或者等于 $b。

0x01 安全问题

1 hash比较缺陷

php在处理hash字符串的时候会用到!=,==来进行hash比较,如果hash值以0e开头,后边都是数字,再与数字比较,就会被解释成0*10^n还是为0,就会被判断相等,绕过登录环节。

root@kali:~/tool# php -r 'var_dump("00e0345" == "0");var_dump("0e123456789"=="0");var_dump("0e1234abc"=="0");'
bool(true)
bool(true)
bool(false)

当全是数字的时候,宽松的比较会执行尽力模式,如0e12345678会被解释成0*10^12345678,除了e不全是数字的时候就不会相等,这能从var_dump("0e1234abc"=="0")可以看出来。

2 bool 欺骗

当存在json_decode和unserialize的时候,部分结构会被解释成bool类型,也会造成欺骗。json_decode示例代码:

$json_str = '{"user":true,"pass":true}';
$data = json_decode($json_str,true);
if ($data['user'] == 'admin' && $data['pass']=='secirity')
{
  print_r('logined in as bool'."\n");
}

运行结果:

root@kali:/var/www# php /root/php/hash.php
logined in as bool

unserialize示例代码:

$unserialize_str = 'a:2:{s:4:"user";b:1;s:4:"pass";b:1;}';
$data_unserialize = unserialize($unserialize_str);
if ($data_unserialize['user'] == 'admin' && $data_unserialize['pass']=='secirity')
{
  print_r('logined in unserialize'."\n");
}

运行结果如下:

root@kali:/var/www# php /root/php/hash.php
logined in unserialize

3 数字转换欺骗

$user_id = ($_POST['user_id']);
if ($user_id == "1")
{
  $user_id = (int)($user_id);
  #$user_id = intval($user_id);
  $qry = "SELECT * FROM `users` WHERE user_id='$user_id';";
}
$result = mysql_query($qry) or die('<pre>' . mysql_error() . '</pre>' );
print_r(mysql_fetch_row($result));

将user_id=0.999999999999999999999发送出去得到结果如下:

Array
(
    [0] => 0
    [1] => lxx'
    [2] =>
    [3] =>
    [4] =>
    [5] =>
)

本来是要查询user_id的数据,结果却是user_id=0的数据。int和intval在转换数字的时候都是就低的,再如下代码:

if ($_POST['uid'] != 1) {
 $res = $db->query("SELECT * FROM user WHERE uid=%d", (int)$_POST['uid']);
 mail(...);
} else {
 die("Cannot reset password of admin");
}

假如传入1.1,就绕过了$_POST['uid']!=1的判断,就能对uid=1的用户进行操作了。另外intval还有个尽力模式,就是转换所有数字直到遇到非数字为止,如果采用:

if (intval($qq) === '123456')
{
  $db->query("select * from user where qq = $qq")
}

攻击者传入123456 union select version()进行攻击。

4 PHP5.4.4 特殊情况

这个版本的php的一个修改导致两个数字型字符溢出导致比较相等

$ php -r 'var_dump("61529519452809720693702583126814" == "61529519452809720000000000000000");'
bool(true)

3 题外话:

同样有类似问题的还有php strcmp函数,manual上是这么解释的,int strcmp ( string $str1 , string $str2 ),str1是第一个字符串,str2是第二个字符串,如果str1小于str2,返回<0,如果str1>str2,返回>0,两者相等返回0,假如str2为一个array呢?

$_GET['key'] = array();
$key = "llocdpocuzion5dcp2bindhspiccy";
$flag = strcmp($key, $_GET['key']);
if ($flag == 0) {
  print "Welcome!";
} else {
  print "Bad key!";
}

运行结果:

root@kali:~/php# php strcmp.php
PHP Warning:  strcmp() expects parameter 2 to be string, array given in /root/php/strcmp.php on line 13
Welcome!

比较多种类型

运算数 1 类型 运算数 1 类型 结果
null 或 string string 将 NULL 转换为 "",进行数字或词汇比较
bool 或 null 任何其它类型 转换为 bool,FALSE 
object object 内置类可以定义自己的比较,不同类不能比较,相同类和数组同样方式比较属性(PHP 4 中),PHP 5 有其自己的说明
string,resource或 number string,resource或 number 将字符串和资源转换成数字,按普通数学比较
array array 具有较少成员的数组较小,如果运算数 1 中的键不存在于运算数 2 中则数组无法比较,否则挨个值比较(见下例)
array 任何其它类型 array 总是更大
object 任何其它类型 object 总是更大
PHP 相关文章推荐
php递归列出所有文件和目录的代码
Sep 10 PHP
PHPMailer邮件类利用smtp.163.com发送邮件方法
Sep 11 PHP
php array_intersect()函数使用代码
Jan 14 PHP
新手学习PHP的一些基础知识分享
Jul 27 PHP
php下拉选项的批量操作的实现代码
Oct 14 PHP
PhpDocumentor 2安装以及生成API文档的方法
May 21 PHP
PHP 快速排序算法详解
Nov 10 PHP
PHP获取文件扩展名的4种方法
Nov 24 PHP
PHP Oauth授权和本地加密实现方法
Aug 12 PHP
PHP基于PDO扩展操作mysql数据库示例
Dec 24 PHP
mysqli扩展无法在PHP7下升级问题的解决
Sep 10 PHP
php的无刷新操作实现方法分析
Feb 28 PHP
thinkPHP模型初始化实例分析
Dec 03 #PHP
ZF框架实现发送邮件的方法
Dec 03 #PHP
PHP实现的蚂蚁爬杆路径算法代码
Dec 03 #PHP
PHP实现QQ空间自动回复说说的方法
Dec 02 #PHP
如何在旧的PHP系统中使用PHP 5.3之后的库
Dec 02 #PHP
thinkphp微信开发(消息加密解密)
Dec 02 #PHP
thinkphp微信开之安全模式消息加密解密不成功的解决办法
Dec 02 #PHP
You might like
php array_merge下进行数组合并的代码
2008/07/22 PHP
基于Discuz security.inc.php代码的深入分析
2013/06/03 PHP
PHP封装的一个支持HTML、JS、PHP重定向的多功能跳转函数
2014/06/19 PHP
PHP使用GD库输出汉字的方法【测试可用】
2016/11/10 PHP
在Laravel的Model层做数据缓存的实现
2019/09/26 PHP
PHP const定义常量及global定义全局常量实例解析
2020/05/28 PHP
通过JQuery实现win8一样酷炫的动态磁贴效果(示例代码)
2013/07/13 Javascript
js获取select标签选中值的两种方式
2014/01/09 Javascript
jQuery实现tag便签去重效果的方法
2015/01/20 Javascript
谈谈encodeURI和encodeURIComponent以及escape的区别与应用
2015/11/24 Javascript
深入分析node.js的异步API和其局限性
2016/09/05 Javascript
gulp加批处理(.bat)实现ng多应用一键自动化构建
2017/02/16 Javascript
微信小程序获取微信运动步数的实例代码
2017/07/20 Javascript
解决IE7中使用jQuery动态操作name问题
2017/08/28 jQuery
js实现Tab选项卡切换效果
2020/07/17 Javascript
Node实战之不同环境下配置文件使用教程
2018/01/02 Javascript
angular2/ionic2 实现搜索结果中的搜索关键字高亮的示例
2018/08/17 Javascript
vue中如何去掉空格的方法实现
2018/11/09 Javascript
跟老齐学Python之集成开发环境(IDE)
2014/09/12 Python
python获取指定时间差的时间实例详解
2017/04/11 Python
Python面向对象程序设计之继承与多继承用法分析
2018/07/13 Python
对python中的six.moves模块的下载函数urlretrieve详解
2018/12/19 Python
Python文本处理简单易懂方法解析
2019/12/19 Python
Pytorch实现基于CharRNN的文本分类与生成示例
2020/01/08 Python
pip安装提示Twisted错误问题(Python3.6.4安装Twisted错误)
2020/05/09 Python
tensorflow转换ckpt为savermodel模型的实现
2020/05/25 Python
Python使用Selenium实现淘宝抢单的流程分析
2020/06/23 Python
3种方式实现瀑布流布局小结
2019/09/05 HTML / CSS
Booking.com美国:全球酒店预订网站
2017/04/18 全球购物
eBay美国官网:eBay.com
2020/10/24 全球购物
买房协议书
2014/04/11 职场文书
二年级学生评语大全
2014/04/23 职场文书
2014年安全工作总结范文
2014/11/13 职场文书
Linux安装Nginx步骤详解
2021/03/31 Servers
Python进阶学习之带你探寻Python类的鼻祖-元类
2021/05/08 Python