详解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 MYSQL 数据备份类
Jun 19 PHP
php基础学习之变量的使用
Jun 09 PHP
php正则表达式(regar expression)
Sep 10 PHP
php addslashes 利用递归实现使用反斜线引用字符串
Aug 05 PHP
PHP验证码函数代码(简单实用)
Sep 29 PHP
PHP+MYSQL实现用户的增删改查
Mar 24 PHP
PHP实现根据银行卡号判断银行
Apr 29 PHP
PHP中$_SERVER使用说明
Jul 05 PHP
PHP中子类重载父类的方法【parent::方法名】
May 06 PHP
php根据数据id自动生成编号的实现方法
Oct 16 PHP
多个Laravel项目如何共用migrations详解
Sep 25 PHP
PHP防止sql注入小技巧之sql预处理原理与实现方法分析
Dec 13 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 iconv函数的使用详解
2013/06/09 PHP
php配置php-fpm启动参数及配置详解
2013/11/04 PHP
保存到桌面、设为桌面且带图标的PHP代码
2013/11/19 PHP
使用php检测用户当前使用的浏览器是否为IE浏览器
2013/12/03 PHP
PHP中遇到BOM、编码导致json_decode函数无法解析问题
2014/07/02 PHP
PHP中使用sleep函数实现定时任务实例分享
2014/08/21 PHP
ThinkPHP框架表单验证操作方法
2017/07/19 PHP
php设计模式之工厂方法模式分析【星际争霸游戏案例】
2020/01/23 PHP
小议Function.apply()之二------利用Apply的参数数组化来提高 JavaScript程序性能
2006/11/30 Javascript
Code: write(s,d) 输出连续字符串
2007/08/19 Javascript
JS支持带x身份证号码验证函数
2008/08/10 Javascript
JavaScript版DateAdd和DateDiff函数代码
2012/03/01 Javascript
用js实现小球的自由移动代码
2013/04/22 Javascript
Javascript 按位与运算符 (&amp;)使用介绍
2014/02/04 Javascript
NodeJS学习笔记之Connect中间件模块(一)
2015/01/27 NodeJs
javascript函数特点实例分析
2015/05/14 Javascript
zTree插件下拉树使用入门教程
2016/04/11 Javascript
深入理解JS DOM事件机制
2016/08/06 Javascript
微信小程序动画组件使用解析,类似vue,且更强大
2019/08/01 Javascript
vue实现树形结构样式和功能的实例代码
2019/10/15 Javascript
[01:48]帕吉至宝加入游戏,遗迹战场现“千劫神屠”
2018/04/07 DOTA
python实现自动登录人人网并访问最近来访者实例
2014/09/26 Python
Python错误: SyntaxError: Non-ASCII character解决办法
2017/06/08 Python
Python实现PS滤镜的旋转模糊功能示例
2018/01/20 Python
Python基于最小二乘法实现曲线拟合示例
2018/06/14 Python
Django中数据库的数据关系:一对一,一对多,多对多
2018/10/21 Python
python爬虫项目设置一个中断重连的程序的实现
2019/07/26 Python
WxPython实现无边框界面
2019/11/18 Python
Selenium 滚动页面至元素可见的方法
2020/03/18 Python
HTML5 表单验证失败的提示语问题
2017/07/13 HTML / CSS
考核评语大全
2014/04/29 职场文书
银行资信证明
2015/06/17 职场文书
2015年环卫处个人工作总结
2015/07/27 职场文书
百年校庆宣传标语口号
2015/12/26 职场文书
护士工作心得体会
2016/01/25 职场文书
如何让你的Nginx支持分布式追踪详解
2022/07/07 Servers