PHP的一些天坑

什么叫天坑。天呐,原来这么坑,不知则已,细思极恐。

一、小数(符点数)不能直接比较是否相等

比如 if( 0.5+0.2==0.7 ) 的结果是 false。究其原因是因为,PHP是基于C语言的,而C语言由于其二进制符点数的表示方式,导致不能精确表示大多数符点数。实际上,几乎所有的编程语言都没能精确表示小数(符点数),这是一个普遍存在的现象,因为这个是 IEEE 754 的缺陷。想要解决此问题,只能另立标准,似乎只有Mathematica解决了此问题。

二、字符串是否相同建议用 === 而非 ==

为什么呢?因为这个比较是弱类型,两个比较时,PHP会先尝试判别左右两者是否为数字。而问题就在于什么样的字符串是数字,是单纯的数字串吗?远远不只于此,还包括 0x 开头的十六进制,XXeX类型的科学记数法 等等,如 ‘12e0‘==‘0x0C‘ 得到的是true。而在数值类型与字符串比较时,甚至一些数字开头的非数值串,比如 12==‘12这个串‘ 得到的值也会是 true。

所以这些情况下,可能会使本来并不相同的字符串被判定为相等。而使用===比较则为包含类型的比较,不会有任何转换,所以是可以准确比较字符串是否相同的。

另外吐槽一下JAVA,==居然比较不了字符串是否相等,因为字符串是一个对象,==变成了判断是否为同一个对象……

三、trim系列函数的过多去除

trim函数的基本用法是去除最外边的空格、换行符之类的。因为其可选参数,很多人也会将其用于去除UTF8BOM头、文件扩展名等等,比如  ltrim($str, "\xEF\xBB\xBF"); rtrim($str, ".txt");  。但是很快,就会发现这些函数会多去除了一些东西,比如本来是想去除后缀的,结果 logtext.txt 会变成了 logte 而不是 logtext。为什么呢?因为后面这个参数的意思不是一个完整字符串,而是字符列表,也就是说会一直检查最左/最右是否符合此列表的其中一个。

那怎么样才是真正我们想要的去掉最前最后呢?网上的说法是说用正则表达式,我封装了对应的三个方法,以便使用。命名规则是比原来PHP的函数多了个s,表示string的意思。用法跟原来PHP的函数一样。

/**
 * 另一种 trim ,不会过多去除
 * $charlist正则元字符会自动转义
 * */
function ltrims($str, $charlist){
    $charlist = quotemeta($charlist);
    return preg_replace("/^{$charlist}/", ‘‘, $str);
}

function rtrims($str, $charlist){
    $charlist = quotemeta($charlist);
    return preg_replace("/{$charlist}$/", ‘‘, $str);
}

function trims($str, $charlist){
    $charlist = quotemeta($charlist);
    $str = preg_replace("/^{$charlist}/", ‘‘, $str);;
    return preg_replace("/{$charlist}$/", ‘‘, $str);
}

function trimBOM($str){
    return preg_replace("/^\xEF\xBB\xBF/", ‘‘, $str);
}

四、网上说的获取客户端IP地址的各种方法

网上流行一段获取客户端IP地址的PHP函数如下:

function getIP() {
    if (getenv(‘HTTP_CLIENT_IP‘)) {
        $ip = getenv(‘HTTP_CLIENT_IP‘);
    }elseif (getenv(‘HTTP_X_FORWARDED_FOR‘)) {
        $ip = getenv(‘HTTP_X_FORWARDED_FOR‘);
    }elseif (getenv(‘HTTP_X_FORWARDED‘)) {
        $ip = getenv(‘HTTP_X_FORWARDED‘);
    }elseif (getenv(‘HTTP_FORWARDED_FOR‘)) {
        $ip = getenv(‘HTTP_FORWARDED_FOR‘);}
    }elseif (getenv(‘HTTP_FORWARDED‘)) {
        $ip = getenv(‘HTTP_FORWARDED‘);
    }else {
        $ip = $_SERVER[‘REMOTE_ADDR‘];
    }
    return $ip;
} 

这函数看起来并没有什么问题,很多开源CMS之类的也在用。然而事实上,问题大着呢!首先第一步,是要了解这些 getenv 读取的东西到底是什么玩意,又是从哪来的。简单来说这些其实是HTTP header,有些代理服务器会把源请求地址放到header里,所以我们服务器可以知道访问用户的原始IP地址。但是,并不是所有代理服务器都会这么做,也并不是只有代理服务器会这么做。

而实际上,这些HTTP header是可以随便改动的,比如curl就可以自己设置各种HTTP header。如果用此函数得到的结果,进行IP限制等操作的话是很轻易绕过的。更可怕的是,如果后续程序没有对此函数取得的IP地址进行格式校验过滤的话,就很微妙地为SQL注入打开了一扇窗户。所以比较保险的方式是只读取非HTTP header的  $_SERVER[‘REMOTE_ADDR‘]

PHP5.4及以上可以使用以下函数判断是否符合IP地址格式 filter_var($ip, FILTER_VALIDATE_IP) ,老版本需自行写正则。

五、foreach的保留现象

使用   foreach($someArr as $someL){ }  之类的用法时,要注意最后的一个 $someL 会一直保留到该函数/方法结束。而当使用引用的时候  foreach($someArr as &$someL){ }这是以引用来保存,也就是说后面若有使用同一个名字的变量名,将会把原数据改变(就像一个乱用的C指针)。为安全起见,建议每个foreach(尤其是引用的)结束之后都使用unset把这些变量清除掉。

foreach($someArr as &$someL){
    //doSomething ...
}unset($someL);

六、htmlspecialchars 函数默认不转义单引号

不少网站都是使用此函数作为通用的输入过滤函数,但是此函数默认情况是不过滤单引号的。这是非常非常地容易造成XSS漏洞。这样的做法和不过滤双引号没太大区别,只要前端写得稍微有点不规范(用了单引号)就会中招。下面这个示例改编自知乎梧桐雨的回答

<!Doctype html><meta charset="utf-8"/><?php $name = $_POST["xxs"];$name = htmlspecialchars($name);
?>
<form action="" method="post">
提交的注入<input type="text" name="xxs" value="ins‘ onclick=‘alert(1)" >
<input type="submit" value="提交" />
</form>

提交后这个按钮会被注入点击弹警告
<input type=‘button‘ value=‘<?=$name?>‘ />

要求所有的时候都使用双引号不得使用单引号,这其实不太现实。所以,这个主要还是后端的责任,把单引号也要转义,我们用的时候一定要给这个函数加上参数  htmlspecialchars( $data, ENT_QUOTES);

很多人向Thinkphp框架提出过这个问题,因为其默认过滤方法就是无参数的htmlspecialchars,不过滤单引号,而其官方答复是“I函数的作用不能等同于防止SQL注入,可以自定义函数来过滤”……毛线啊,最基本的防护都不给力,这是给埋了多少隐患啊。在此强烈各位使用者重新定义默认过滤函数,我自己定义的是 htmlspecialchars(trim($data), ENT_QUOTES); ,有更好建议欢迎评论。同时非常希望TP官方更正此问题。

关于XSS,容我多说两句,请看下面这个例子。

<?php $name=‘alert(1)‘; ?>
<p id="XSS2"></p>
<script src="//cdn.batsing.com/jquery.js"></script>
<script>
$("#XSS2")[0].innerHTML = <?=$name?>;$("#XSS2").html( <?=$name?> );
$("#XSS2")[0].innerHTML = "<?=$name?>";$("#XSS2").html(" <?=$name?> ");
</script>

其中第1、2行 JS会造成 XSS 漏洞,第3、4行则不会。而  alert(1) 这样一种字符串,后端甚至没有什么比较好的方法可以过滤,唯一有效的方法可能是在数据的两端加上引号。主要责任还是在于前端,对 innerHTML 和 jQuery的html() 的输出使用时,一定要确保传入的参数是字符串,否则其危险性不亚于 eval 函数

时间: 2025-01-12 01:14:29

PHP的一些天坑的相关文章

vue-cli&amp;webpack&amp;arcgis API For JS的天坑之路(一)

写在前面的话(背景交代) 最近参加esri比赛,但是又想趁机接触前端最新的一些框架和技术,所以,毅然决然的踏上了这个天坑之路.我现在只是成功的把地图渲染出来了,所以,我也不知道会不会有天坑二的出现. gituhb项目地址 新建vue-cli工程 如何用vue-cli + webpack构建一个工程,网上一大堆的代码,我就不赘述了.比如这个就是很好地入门文章,先要做的还是要把vue-cli和webpack的模块划分好,框架搭建好,然后才是我要说的,地图部分. 如何在vue-cli,webpack中

vue-cli&amp;webpack&amp;arcgis API For JS的天坑之路(二)

背景交代 果然,不是这么容易的,这么快天坑二就粗来了.现在我要完成一个功能,移动地图点击图上按钮,复位地图,涉及到的问题是,如何在地图加载后再添加这个按钮,否则地图还未加载,按钮已经出现,点击了要出大问题啊!同志,由于我对api不熟悉,所以我不知道api是否直接提供这样的按钮接口,所以我自己上. 实现 要解决这个问题,首先是翻阅api的文档,查找类似load事件的东西,这个是肯定有的, load事件中有一个loaded属性,当地图加载完成后,loaded属性会变为true 但是在vue中如何监听

Android新项目GBSS:第3篇 Tomcat连接数据库[天坑,耗了1天才过关]

搭建好了开发环境,设计好了数据库,接下来该连接数据库了,这一块细节也基本上忘光,只能求助于Google了,大天朝最近围墙建的越来越高,Google基本处于不可用状态,SBBD又只能用来搜广告,谁能拯救我等屌丝码农. 步骤1:将mysql-connector-java-5.1.34-bin.jar复制到$CATALINA_HOME/lib文件夹下.下表列出了MySQL JDBC官方驱动和MySQL Server的版本对应关系[突然发现:有时候上QQ只是为了截图] 步骤2:配置JNDI数据源和连接池

CSS 天坑 I - 字体单位

首先,本文所讨论的“坑”是在做回应式网页设计( Responsive Web Design 以下简称 RWD)时显现的,如果你还只是在做传统的Web设计这算不上是一个坑,因为传统的Web页面是死的,不会自动调节不能适应各类设备屏幕尺寸自然不会产生任何尺寸变化的问题.相反地,要为不同设备上的用户提供最好的阅读或使用体验我们不可避免的就是对元素或字体的尺寸的测量与控制.由其是字体的尺寸,不知道你是否有以下的经历 同样的一个网页,同一份样式表,但: 字体在mac 上很漂亮,但在windows很难看 ?

mvc.net路由中带特殊字符如【.*/\】等时遇到的天坑

用mvc.net的路由做网站伪静态时出现的天坑,自己一直没测试出来,竟然要靠客户被坑了后才知道 解决办法 参考https://stackoverflow.com/questions/16581184/mvc4-404-errors得到最安全的解决办法是: <system.webServer> <handlers> <!--推荐这里的配置,虽然麻烦点,但是安全,有效--> <add name="UrlRoutingHandler" type=&q

Brave_Cattle还得填好的天坑

Brave_Cattle太菜了,还有很多坑要填,于是做了一个列表提醒自己. LIST 字符串是一个天坑 AC自动机 后缀数组 回文树 数据结构 树套树 虚树 莫队 基本不会的数学 FFT以及相关 组合数学 期望 数论 Miller_rabbin 计算几何 还有一大堆待添加... 原文地址:https://www.cnblogs.com/BCOI/p/9215701.html

生化环材四大天坑劝退指南

作者:弗兰克扬 十几年前我还在玩论坛的时候,我们作为有管理权限的小版主都知道,一个优秀的经验交流社区最重要的一个帖子就是FAQ,这个帖子被置顶放在一个版面的最上面是一种无声的宣告,新来者若想在本版进行深入有效有趣的交流,请首先进来学习FAQ,不要问重复的问题,不要做无脑伸手党.但是这些年我渐渐发现,许多网站都消失了FAQ这种东西,大家像月经一样问着每月都有人问,每年都有人问的问题,然后重复着同样无聊的争论. 我已经习惯了每天都至少有十几个人在知乎提出这种已经被提问和回答了几万遍的问题了. 一方面

boost里面的天坑

最近在做一个网络相关的项目, cs架构的.调试的时候发现, 在client大规模的给server发数据的时候, server会收到乱序的数据.为了定位bug, 在client发往server的消息头里面加入了消息序号字段,  在server, client端分别输出每条消息的头, 内容信息, 以及分别抓包.比较郁闷的是, 发现在server端收到的包里面的消息序号字段有重复的, 进一步跟踪发现, client应用层是没有问题的, 但是到了网络层就出问题了. 看着这个现象, 有点像线程同步的问题,

【模板整合】【及时更新】【天坑】计算几何模板

计算几何模板要写的内容真多- 我写烦了-先写这些放上来吧- #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #define MAXDBL 1e20 #define eps 1e-9 #define pi acos(-1) using namespace std; stru