详解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 XML error parsing SOAP payload on line 1
Jun 17 PHP
php中通过curl smtp发送邮件
Jun 05 PHP
PHP5.4中json_encode中文转码的变化小结
Jan 30 PHP
php正则取img标记中任意属性(正则替换去掉或改变图片img标记中的任意属性)
Aug 13 PHP
thinkphp3.0输出重复两次的解决方法
Dec 19 PHP
PHP获取youku视频真实flv文件地址的方法
Dec 23 PHP
php实现utf-8转unicode函数分享
Jan 06 PHP
php相对当前文件include其它文件的方法
Mar 13 PHP
php通过文件流方式复制文件的方法
Mar 13 PHP
Yii2 assets清除缓存的方法
May 16 PHP
php redis实现文章发布系统(用户投票系统)
Mar 04 PHP
php 输出缓冲 Output Control用法实例详解
Mar 03 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
谏山创故乡大分县日田市水坝将设立《进击的巨人》立艾伦、三笠以及阿尔敏的铜像!
2020/03/06 日漫
从PHP的源码中深入了解stdClass类
2014/04/18 PHP
学习php设计模式 php实现适配器模式
2015/12/07 PHP
PHP实现的构造sql语句类实例
2016/02/03 PHP
浅谈Laravel模板实体转义带来的坑
2019/10/22 PHP
javascript显示选择目录对话框的代码
2008/11/10 Javascript
用正则表达式 动态创建/增加css style script 兼容IE firefox
2009/03/10 Javascript
调试JavaScript中正则表达式中遇到的问题
2015/01/27 Javascript
JavaScript检查弹出窗口是否被阻拦的方法技巧
2015/03/13 Javascript
Jquery操作cookie记住用户名
2016/03/29 Javascript
JavaScript中的this陷阱的最全收集并整理(没有之一)
2017/02/21 Javascript
Vue通过input筛选数据
2020/10/26 Javascript
《javascript少儿编程》location术语总结
2018/05/27 Javascript
详解Vue组件之作用域插槽
2018/11/22 Javascript
JavaScript进阶(一)变量声明提升实例分析
2020/05/09 Javascript
vue单元格多列合并的实现
2020/11/26 Vue.js
通过滑动翻页效果实现和移动端click事件问题
2021/01/26 Javascript
[46:44]DOTA2-DPC中国联赛 正赛 Ehome vs PSG.LGD BO3 第二场 3月7日
2021/03/11 DOTA
Centos5.x下升级python到python2.7版本教程
2015/02/14 Python
Python实现的朴素贝叶斯算法经典示例【测试可用】
2018/06/13 Python
python 提取文件指定列的方法示例
2019/08/07 Python
Python 基于FIR实现Hilbert滤波器求信号包络详解
2020/02/26 Python
tensorflow 动态获取 BatchSzie 的大小实例
2020/06/30 Python
Python使用urlretrieve实现直接远程下载图片的示例代码
2020/08/17 Python
Python调用飞书发送消息的示例
2020/11/10 Python
HTML5+css3:3D旋转木马效果相册
2017/01/03 HTML / CSS
Saucony澳大利亚官网:美国跑鞋品牌,运动鞋中的劳斯莱斯
2018/05/05 全球购物
美国知名的旅游网站:OneTravel
2018/10/09 全球购物
整个世界的设计师家具在哈恩:Designathome
2019/03/25 全球购物
N:Philanthropy官网:美国洛杉矶基础款服装
2020/06/09 全球购物
澳大利亚头发和美容产品购物网站:OZ Hair & Beauty
2020/03/27 全球购物
高分子材料个人求职信范文
2013/09/25 职场文书
国税会议欢迎词
2014/01/16 职场文书
学校运动会霸气口号
2014/06/07 职场文书
2014年党风廉政建设工作总结
2014/11/19 职场文书
大学生党员个人总结
2015/02/13 职场文书