最近php爆了一个漏洞——“Magic Hash”,漏洞描述如下:
PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以0E开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
由于php对字符串进行hash处理后,如md5方法,会返回一个十六进制字符组成的字符串,如d41d8cd98f00b204e9800998ecf8427e, d41d8cd98f00b204e9800998ecf8427e等,而当hash得到的值是以0e开头任意数字结尾时,会认为这两个字符串相等:
其实这个漏洞并不怪php,因为e在数字中,代表的就是10的多少次方,php的比较运算符==对两边的字符串进行了类型转换,php官方手册也给出了明确示例:
4月8日,wordpress也发布了一个重要的更新,这次更新中修复了一系列安全问题,其中一个就是因为==而引起的用户信息伪造漏洞。
在工作中,我经常会遇到php新手甚至老手写出来有类似问题的代码,因为他们没有清晰地认识到php的原理,我也不止一次的和各种人强调这个问题,可是如同“野火烧不尽”般,席卷漏洞、测试bug。普通的程序员,犯错误会及时改正,而优秀的程序员犯错误会及时思考,避免以后再犯。废话不多说,切入正题普及知识吧!
php提供了两种比较运算符==和===,三个等号就是严格判断,对于上面的示例,三个等号都会返回false。因此,我们在平时使用中,要根据我们的业务场景,正确的使用==和===。对于松散的比较,php给出了一个表格。
根据表格,我们重点分析如下几个case:
这是因为,在==进行对比时,如果两边类型不一致,php会对变量进行类型转换。因此数字0和字符串"php"比较时,将字符串"php"转换成了数字0。
即如果$a == $b, $b == $c,不代表$a == $c一定成立。
因为0 == "0",0 == NULL,而"0" != NULL。
"1000000" < "a" "a" < 1 1 < "1000000" 以上三个表达式都会返回true,这是因为两个字符进行大小比较时,是比较着这两个字符的ASCII码大小。当一个数字与一个字符串进行大小比较时,会将其转换为数字。
如此不同的两个订单号,竟然被判断成相等。这是因为当==两边的变量即使是字符串时,如果这两个变量看起来就是数字时,它会把它当做数字来处理。
而E-5230代表了10的-5230次方,E-1743"代表了10的-1743次方,两边都被当做了数字0,多么悲催的判断!
举例,in_array("0E12", array(0, 12)),会返回true。
如果想进行强制判断,in_array是接受第三个参数的,第三个参数设为true,就可以打开强制类型转换机制:
in_array("0E12", array(0, 12), true),会返回false。
不仅仅是php,对于弱语言类型的编程语言,都会有类似的这样那样的坑。弱语言本身提供了强大的灵活性,就是因为这强大的灵活性,导致很多时候不可控制。任何语言都有其自己的特点,优势发挥到极致便是劣势,不论选择哪种语言,坑永远都在,唯一不变的是我们随着时间积累的经验!
Leave a Reply