本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/92
了解下hash_equals的概念:
bool hash_equals ( string $known_string , string $user_string )
比较两个字符串,无论它们是否相等,本函数的时间消耗是恒定的。
本函数可以用在需要防止时序攻击的字符串比较场景中, 例如,可以用在比较 crypt() 密码哈希值的场景。
上面提到了时序攻击,什么是时序攻击呢?引自如何通俗地解释时序攻击(timing attack)?中shotgun的回答:
举一个最简单的计时攻击的例子,某个函数负责比较用户输入的密码和存放在系统内密码是否相同,如果该函数是从第一位开始比较,发现不同就立即返回,那么通过计算返回的速度就知道了大概是哪一位开始不同的,这样就实现了电影中经常出现的按位破解密码的场景。密码破解复杂度成千上万倍甚至百万千万倍的下降。
而hash_equals的时间消耗是恒定的,也就防止了时序攻击。
PHP 中的 == 和“隐式转换”
<?php
var_dump(md5('240610708') == md5('QNKCDZO')); //true
为什么会是true?
md5('240610708') 的结果是:0e462097431906509019562988736854
md5('QNKCDZO') 的结果是:0e830400451993494058024219903391
在PHP中, 在使用 == 号时,如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。
php会将以0x开头的或者0e开头的并且后面都是数字的(^0e\d*\(,^0x\d*\))当成数字来处理。上面两个例子都被等效成了 0×10^0 ,所以相等。
类似的还有
var_dump('0x1234Ab' == '1193131'); //true
为了防止此现象,需要使用===或者hash_equals
函数来处理。
误解
var_dump(0 == "0e1111111"); //true
var_dump("0" == "0e1111111"); //true
之前一直认为输出结果是true false。但是实验发现两个都是true。原来php在将字符串转换成整数的条件不光是字符串和整数比较。还有当两个数字型字符串比较时,也会将两个字符串都转换成整数。
引用网上一张图: