详解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相关资料
Oct 09 PHP
采集邮箱的php代码(抓取网页中的邮箱地址)
Jul 17 PHP
php中将字符串转为HTML的实体引用的一个类
Feb 03 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(九)
Jun 24 PHP
PHP中使用addslashes函数转义的安全性原理分析
Nov 03 PHP
php打印一个边长为N的实心和空心菱型的方法
Mar 02 PHP
php自定义hash函数实例
May 05 PHP
PHP获取某个月最大天数(最后一天)的方法
Jul 29 PHP
php中preg_replace正则替换用法分析【一次替换多个值】
Jan 17 PHP
Yii2框架实现数据库常用操作总结
Feb 08 PHP
PHP 使用二进制保存用户状态的实例
Jan 29 PHP
PHP常用字符串输出方法分析(echo,print,printf及sprintf)
Mar 09 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中获取变量的变量名的一段代码的bug分析
2011/07/07 PHP
深入php self与$this的详解
2013/06/08 PHP
Yii2前后台分离及migrate使用(七)
2016/05/04 PHP
jsTree树控件(基于jQuery, 超强悍)[推荐]
2009/09/01 Javascript
js 中{},[]中括号,大括号使用详解
2011/05/12 Javascript
深入理解JavaScript系列(48):对象创建模式(下篇)
2015/03/04 Javascript
javascript中类的定义方式详解(四种方式)
2015/12/22 Javascript
原生 JS Ajax,GET和POST 请求实例代码
2016/06/08 Javascript
老生常谈javascript的类型转换
2016/10/12 Javascript
vue多级多选菜单组件开发
2020/09/08 Javascript
快速入门Vue
2016/12/19 Javascript
javascript编写简易计算器
2017/05/06 Javascript
使用sessionStorage解决vuex在页面刷新后数据被清除的问题
2018/04/13 Javascript
如何从零开始利用js手写一个Promise库详解
2018/04/19 Javascript
nodejs对mongodb数据库的增加修删该查实例代码
2020/01/05 NodeJs
在Uni中使用Vue的EventBus总线机制操作
2020/07/31 Javascript
[07:55]2014DOTA2 TI正赛第三日 VG上演推进荣耀DKEG告别
2014/07/21 DOTA
[46:12]完美世界DOTA2联赛循环赛 DM vs Matador BO2第一场 11.04
2020/11/04 DOTA
Python用UUID库生成唯一ID的方法示例
2016/12/15 Python
浅谈Python 的枚举 Enum
2017/06/12 Python
python实现朴素贝叶斯分类器
2018/03/28 Python
基于MTCNN/TensorFlow实现人脸检测
2018/05/24 Python
python求解数组中两个字符串的最小距离
2018/09/27 Python
pycharm 解除默认unittest模式的方法
2018/11/30 Python
django admin组件使用方法详解
2019/07/19 Python
python异常处理、自定义异常、断言原理与用法分析
2020/03/23 Python
给keras层命名,并提取中间层输出值,保存到文档的实例
2020/05/23 Python
python如何删除文件、目录
2020/06/23 Python
python Xpath语法的使用
2020/11/26 Python
德国骆驼商店:ActiveFashionWorld
2017/11/18 全球购物
香港通票:Hong Kong Pass
2019/02/26 全球购物
哄娃神器4moms商店:美国婴童用品品牌
2019/03/07 全球购物
竞选班长自荐书范文
2014/03/09 职场文书
运动会通讯稿300字
2015/07/20 职场文书
MySQL非空约束(not null)案例讲解
2021/08/23 MySQL
vue中this.$http.post()跨域和请求参数丢失的解决
2022/04/08 Vue.js