详解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安全配置 如何配置使其更安全
Dec 16 PHP
UCenter 批量添加用户的php代码
Jul 17 PHP
PHP 读取Postgresql中的数组
Apr 14 PHP
php发送post请求函数分享
Mar 06 PHP
smarty模板引擎中变量及变量修饰器用法实例
Jan 22 PHP
PHP中COOKIES使用示例
Jul 26 PHP
php通过排列组合实现1到9数字相加都等于20的方法
Aug 03 PHP
我整理的PHP 7.0主要新特性
Jan 07 PHP
XHProf报告字段含义的解析
May 17 PHP
基于PHP实现用户注册登录功能
Oct 14 PHP
PHP基于redis计数器类定义与用法示例
Feb 08 PHP
PHP autoload使用方法及步骤详解
Sep 05 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
4月1日重磅发布!《星际争霸II》6.0.0版本更新
2020/04/09 星际争霸
PHP-MySQL教程归纳总结
2008/06/07 PHP
php strlen mb_strlen计算中英文混排字符串长度
2009/07/10 PHP
php5 apache 2.2 webservice 创建与配置(java)
2011/01/27 PHP
PHP下判断网址是否有效的代码
2011/10/08 PHP
php计算给定时间之前的函数用法实例
2015/04/03 PHP
Ajax+PHP实现的模拟进度条功能示例
2019/02/11 PHP
比较简单实用的使用正则三种版本的js去空格处理方法
2007/11/18 Javascript
json传值以及ajax接收详解
2016/05/24 Javascript
利用Jquery队列实现根据输入数量显示的动画
2016/09/01 Javascript
bootstrap模态框跳转到当前模板页面 框消失了而背景存在问题的解决方法
2020/11/30 Javascript
Vue Transition实现类原生组件跳转过渡动画的示例
2017/08/19 Javascript
JS解决position:sticky的兼容性问题的方法
2017/10/17 Javascript
微信小程序wx.getImageInfo()如何获取图片信息
2018/01/26 Javascript
axios取消请求的实践记录分享
2018/09/26 Javascript
Postman的下载及安装教程详解
2018/10/16 Javascript
在Vant的基础上实现添加表单验证框架的方法示例
2018/12/05 Javascript
详解关于Vuex的action传入多个参数的问题
2019/02/22 Javascript
vue2.0基于vue-cli+element-ui制作树形treeTable
2019/04/30 Javascript
详解小程序如何改变onLoad的执行时机
2019/11/01 Javascript
如何在Node和浏览器控制台中打印彩色文字
2020/01/09 Javascript
图文详解WinPE下安装Python
2016/05/17 Python
梯度下降法介绍及利用Python实现的方法示例
2017/07/12 Python
python MySQLdb使用教程详解
2018/03/20 Python
Python实现合并同一个文件夹下所有PDF文件的方法示例
2018/04/28 Python
python 以16进制打印输出的方法
2018/07/09 Python
Python动态生成多维数组的方法示例
2018/08/09 Python
在Python运行时动态查看进程内部信息的方法
2019/02/22 Python
详解PyTorch手写数字识别(MNIST数据集)
2019/08/16 Python
Python模块相关知识点小结
2020/03/09 Python
SmartBuyGlasses荷兰:购买太阳镜和眼镜
2020/03/16 全球购物
一套PHP的笔试题
2013/05/31 面试题
小学信息技术教学反思
2014/02/10 职场文书
入党转正介绍人意见
2015/06/03 职场文书
CSS3 Tab动画实例之背景切换动态效果
2021/08/23 HTML / CSS
HTML静态页面获取url参数和UserAgent的实现
2022/08/05 HTML / CSS