详解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 相关文章推荐
在任意字符集下正常显示网页的方法一
Apr 01 PHP
php 面向对象的一个例子
Apr 12 PHP
在yii中新增一个用户验证的方法详解
Jun 20 PHP
浅析虚拟主机服务器php fsockopen函数被禁用的解决办法
Aug 07 PHP
Thinkphp中的volist标签用法简介
Jun 18 PHP
使用ThinkPHP+Uploadify实现图片上传功能
Jun 26 PHP
PHP文件上传判断file是否己选择上传文件的方法
Nov 10 PHP
php设计模式之单例模式实例分析
Feb 25 PHP
phplist及phpmailer(组合使用)通过gmail发送邮件的配置方法
Mar 30 PHP
php+mysql实现的二级联动菜单效果详解
May 10 PHP
php识别翻转iphone拍摄的颠倒图片
May 17 PHP
详解PHP的抽象类和抽象方法以及接口总结
Mar 15 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
Banner程序
2006/10/09 PHP
最常用的8款PHP调试工具
2014/07/06 PHP
javascript获取当前日期时间及其它操作函数
2011/01/11 Javascript
JQuery事件e参数的方法preventDefault()取消默认行为
2013/09/26 Javascript
NodeJS学习笔记之MongoDB模块
2015/01/13 NodeJs
javascript实现checkBox的全选,反选与赋值
2015/03/12 Javascript
jquery+css3实现网页背景花瓣随机飘落特效
2015/08/17 Javascript
bootstrap多种样式进度条展示
2016/12/20 Javascript
基于JavaScript实现熔岩灯效果导航菜单
2017/01/04 Javascript
原生Javascript插件开发实践
2017/01/09 Javascript
JavaScript数组去重的6个方法
2017/01/21 Javascript
使用jquery判断一个元素是否含有一个指定的类(class)实例
2017/02/12 Javascript
three.js中3D视野的缩放实现代码
2017/11/16 Javascript
详解三种方式解决vue中v-html元素中标签样式
2018/11/22 Javascript
jquery实现动态改变css样式的方法分析
2019/05/27 jQuery
vue实现在v-html的html字符串中绑定事件
2019/10/28 Javascript
微信公众号H5之微信分享常见错误和问题(小结)
2019/11/14 Javascript
vue中实现图片压缩 file文件的方法
2020/05/28 Javascript
解决vue项目 build之后资源文件找不到的问题
2020/09/12 Javascript
[04:03][TI9趣味短片] 小鸽子茶话会
2019/08/20 DOTA
Python网络爬虫出现乱码问题的解决方法
2017/01/05 Python
Python实现删除文件中含“指定内容”的行示例
2017/06/09 Python
Python3实现简单可学习的手写体识别(实例讲解)
2017/10/21 Python
Python Lambda函数使用总结详解
2019/12/11 Python
浅谈对python中if、elif、else的误解
2020/08/20 Python
html5+css3实现一款注册表单实例
2013/04/17 HTML / CSS
澳大利亚男士西服品牌:M.J.Bale
2018/02/06 全球购物
德国亚洲食品网上商店:asiafoodland.de
2019/12/28 全球购物
以下为Windows NT 下的32 位C++程序,请计算sizeof 的值
2016/12/07 面试题
Ado与Ado.net的相同与不同
2014/12/08 面试题
本科毕业生的求职信范文
2013/11/20 职场文书
淘宝客服自我总结鉴定
2014/01/25 职场文书
党员批评与自我批评材料
2014/10/14 职场文书
六年级学生评语大全
2014/12/26 职场文书
公司行政助理岗位职责
2015/04/11 职场文书
2015年教研员工作总结
2015/05/26 职场文书