详解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中一个有意思的日期逻辑处理
Mar 25 PHP
php json_encode值中大括号与花括号区别
Sep 30 PHP
php循环创建目录示例分享(php创建多级目录)
Mar 04 PHP
PHP程序漏洞产生的原因分析与防范方法说明
Mar 06 PHP
PHP实现的MongoDB数据库操作类分享
May 12 PHP
使用GD库生成带阴影文字的图片
Mar 27 PHP
浅谈使用PHP开发微信支付的流程
Oct 04 PHP
PHP Mysqli 常用代码集合
Nov 12 PHP
Zend Framework处理Json数据方法详解
Dec 09 PHP
学习thinkphp5.0验证类使用方法
Nov 16 PHP
laravel-admin select框默认选中的方法
Oct 03 PHP
php中配置文件保存修改操作 如config.php文件的读取修改等操作
May 12 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
js 省地市级联选择
2010/02/07 Javascript
Extjs中常用表单介绍与应用
2010/06/07 Javascript
JSON 数据格式介绍
2012/01/13 Javascript
页面元素绑定jquery toggle后元素隐藏的解决方法
2014/03/27 Javascript
js实现特定位取反原理及示例
2014/06/30 Javascript
js鼠标点击图片实现随机变换图片的方法
2015/02/16 Javascript
JS基于FileSystemObject创建一个指定路径的TXT文本文件
2015/08/05 Javascript
全面解析Bootstrap手风琴效果
2020/04/17 Javascript
Bootstrap布局组件教程之Bootstrap下拉菜单
2016/06/12 Javascript
Vue+Koa2+mongoose写一个像素绘板的实现方法
2019/09/10 Javascript
js+springMVC 提交数组数据到后台的实例
2019/09/21 Javascript
uploadify插件实现多个图片上传并预览
2019/09/30 Javascript
JS删除数组指定值常用方法详解
2020/06/04 Javascript
使用React-Router实现前端路由鉴权的示例代码
2020/07/26 Javascript
Python迭代用法实例教程
2014/09/08 Python
python基于BeautifulSoup实现抓取网页指定内容的方法
2015/07/09 Python
利用Python脚本生成sitemap.xml的实现方法
2017/01/31 Python
Python3的介绍、安装和命令行的认识(推荐)
2018/10/20 Python
详解Python中pandas的安装操作说明(傻瓜版)
2019/04/08 Python
Django框架视图函数设计示例
2019/07/29 Python
PyCharm2019安装教程及其使用(图文教程)
2019/09/29 Python
Python3 main函数使用sys.argv传入多个参数的实现
2019/12/25 Python
完美解决python针对hdfs上传和下载的问题
2020/06/05 Python
python闭包与引用以及需要注意的陷阱
2020/09/18 Python
Python Tkinter实例——模拟掷骰子
2020/10/24 Python
css3进行截取替代js的substring
2013/09/02 HTML / CSS
KEETSA环保床垫:更好的睡眠,更好的生活!
2016/11/24 全球购物
如何利用find命令查找文件
2015/02/07 面试题
千元咖啡店的创业计划书范文
2013/12/29 职场文书
通用自荐信范文
2014/03/14 职场文书
个人整改措施书面材料
2014/10/24 职场文书
2014年食品安全工作总结
2014/12/04 职场文书
python 通过使用Yolact训练数据集
2021/04/06 Python
如何用Navicat操作MySQL
2021/05/12 MySQL
nginx安装以及配置的详细过程记录
2021/09/15 Servers
python使用opencv对图像添加噪声(高斯/椒盐/泊松/斑点)
2022/04/06 Python