常用知识 - 字符串与数字0比较要注意 ¶
作者:KK
发表日期:2016.7.20
先看代码:
var_export([
'a == 0' => 'a' == 0, //true
'abcd == 0' => 'abcd' == 0, //true
'中文 == 0' => '中文' == 0, //true
'a1 == 0' => 'a1' == 0, //true
'a123456 == 0' => 'a123456' == 0, //true
'===接下来数字开头===' => '============',
'0a == 0' => '0a' == 0, //true
'012a == 0' => '012a' == 0, //false
'012a == 12' => '012a' == 12, //true
'012a == 0' => '012a' == 0, //false
'123 == 0' => '123' == 0, //false
'123 == 123' => '123' == 123, //true
]);
运行结果:
结论:字母开头的字符串 与 数字0 用 == 模糊比较总是true
¶
详解 ¶
不管是'a' == 0
还是0 == 'a'
,只要是两个比较值中有一个是数字,而另一个是字符串,那么PHP会先自动将字符串转换成一个数字,然后再与数字比较,由于a这个字符串转成数字后的值是0,所以最终变成了0 == 0
结果就成了true
带来的坑 ¶
例子:
$age = $_POST['age']; // 8a
if($age <= 0){
exit('请输入修改的年龄');
}
$db->insert('user', ['age' => $age]);
这样数据库就会构建成一个无效的插入语句报错了
再假设一下如果$age的值如果是8 or 1 = 1
,然后执行查找会怎样?
$age = $_POST['age']; // 8a
if($age <= 0){
exit('请输入修改的年龄');
}
$db->select('user', ['age' => $age]);
//如果没用PDO参数绑定或者其它参数化查询,底层直接简单拼字符串的话绝对会这样:
SELECT * FROM `user` WHERE age = 8 or 1 = 1
//还有的用了框架的人都途方便,这样写:
(new \yii\db\Query())->from('user')->where('age = ' . $age)->one(); //有注入漏洞,其它框架就不举例子了
//这样写where条件才安全
(new \yii\db\Query())->from('user')->where(['age' => $age])->one(); //注入会失败,
你懂的,主要是这个数字开头的注入字符串绕过了比较逻辑
看你还敢不敢随便拼字符串条件?能用数组条件就尽量用数组条件,一般框架的底层都会做好注入过滤和参数化查询这些事情
这只是拿注入攻击来举个例子,不止是注入,光是让你程序跑出错都有很多种可能,试想,一个我们本应预期是数字的值,居然成了字符串然后被流通到后面的环节中,肯定存在很多影响计算结果的可能性,所以在源头就应该确保它是数字
解决办法 ¶
当然是将
==
比较符写成===
啦但要注意这其实只是第一思考结果,但实际上程序员们都习惯了写
==
号,所以不能保证所有团队成员或者新加入员工都能这样去写===的严格比较并且你也基本没办法每处都检查他的比较符号有没有写对
将请求参数强制转换
我的做法通常都是在接收前端参数时就将它强转成int:
$age = (int)$this->post('age'); //获取$_POST['age'];
这样就算传来字符串也会被转成数字类型,就当它是那个数字处理咯,要不就用is_numerice这些函数进行严格的内容验证,自己看性价比取舍,但这个防御要有!
下面是我项目中的一些例子,也强制性要求项目成员收参数时必须按照相关类型先强行转换