详解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中的字符串函数
Nov 24 PHP
php中的登陆login
Jan 18 PHP
用来解析.htgroup文件的PHP类
Sep 05 PHP
PHP不用递归实现无限分级的例子分享
Apr 18 PHP
PHP防止刷新重复提交页面的示例代码
Nov 11 PHP
php+Memcached实现简单留言板功能示例
Feb 15 PHP
PHP基于自增数据如何生成不重复的随机数示例
May 19 PHP
php 中的closure用法详解
Jun 12 PHP
Lumen timezone 时区设置方法(慢了8个小时)
Jan 20 PHP
PHP解析url并得到url参数方法总结
Oct 11 PHP
PHP调用全国天气预报数据接口查询天气示例
Feb 20 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正则获取页面所有图片地址
2016/03/23 PHP
CI分页类首页、尾页不显示的解决方法
2016/03/28 PHP
Jquery实战_读书笔记1—选择jQuery
2010/01/22 Javascript
Jquery 点击按钮自动高亮实现原理及代码
2014/04/25 Javascript
js中substr,substring,indexOf,lastIndexOf,split,replace的用法详解
2015/11/09 Javascript
iscroll碰到Select无法选择下拉刷新的解决办法
2016/05/21 Javascript
功能强大的Bootstrap使用手册(一)
2016/08/02 Javascript
JavaScript如何实现图片懒加载(lazyload) 提高用户体验(增强版)
2016/11/30 Javascript
js实现点击按钮弹出上传文件的窗口
2016/12/23 Javascript
JavaScript中的遍历详解(多种遍历)
2017/04/07 Javascript
Vue 按键修饰符处理事件的方法
2018/05/04 Javascript
angularjs实现对表单输入改变的监控(ng-change和watch两种方式)
2018/08/29 Javascript
JavaScript实现获取两个排序数组的中位数算法示例
2019/02/26 Javascript
JavaScript 面向对象基础简单示例
2019/10/02 Javascript
js实现鼠标切换图片(无定时器)
2021/01/27 Javascript
[49:40]2018DOTA2亚洲邀请赛小组赛 A组加赛 TNC vs Newbee
2018/04/03 DOTA
[01:10]为家乡而战!完美世界城市挑战赛全国总决赛花絮
2019/07/25 DOTA
Python实现的彩票机选器实例
2015/06/17 Python
Apache如何部署django项目
2017/05/21 Python
Python原始字符串与Unicode字符串操作符用法实例分析
2017/07/22 Python
PyQt5每天必学之布局管理
2018/04/19 Python
Anaconda 离线安装 python 包的操作方法
2018/06/11 Python
python清除函数占用的内存方法
2018/06/25 Python
Python调用.NET库的方法步骤
2019/12/27 Python
python实现将字符串中的数字提取出来然后求和
2020/04/02 Python
在keras里面实现计算f1-score的代码
2020/06/15 Python
Python基于time模块表示时间常用方法
2020/06/18 Python
html5 Canvas画图教程(7)—canvas里画曲线之quadraticCurveTo方法
2013/01/09 HTML / CSS
别名指示符是什么
2012/10/08 面试题
个人委托书范本
2014/09/13 职场文书
自主招生专家推荐信
2015/03/26 职场文书
2015年乡镇妇联工作总结
2015/05/19 职场文书
html+css实现分层金字塔的实例
2021/06/02 HTML / CSS
解决persistence.xml配置文件修改存放路径的问题
2022/02/24 Java/Android
Java 关于String字符串原理上的问题
2022/04/07 Java/Android
tomcat默认最大连接数及相关调整方法
2022/05/06 Servers