php实现禁止IP段访问网站的代码

 更新时间:2016年11月25日 15:24  点击:1633
有个前提条件是我们的页面必须是php类型的页面,如果你生成了html静态页面这种方法就不可行了哦,下面我们来看看php实现禁止IP段访问网站的代码.
 代码如下 复制代码

<?php
//加IP访问限制
if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
$userip = getenv('HTTP_CLIENT_IP');
} elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
$userip = getenv('HTTP_X_FORWARDED_FOR');
} elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
$userip = getenv('REMOTE_ADDR');
} elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
$userip = $_SERVER['REMOTE_ADDR'];
}
$ban_range_low=ip2long("217.0.0.0"); //ip段上
$ban_range_up=ip2long("217.255.255.255");//ip段尾
$ip=ip2long($userip]);
if ($ip>$ban_range_low && $ip<$ban_range_up)
{
print "Banned";
exit();
}
?>
文章介绍了大致的关于如何在 php.ini中做一些常用的安全配置,有需要的同学可参考一下。

(1)打开php的安全模式

php的安全模式是个非常重要的内嵌的安全机制,能够控制一些php中的函数,比如system(),同时把很多文件操作函数进行了权限控制,也不允许对某些关键字文件的文件,比如/etc/passwd,但是默认的php.ini是没有打开安全模式的,我们把它打开:

safe_mode = on

(2)用户组安全

当safe_mode打开时,safe_mode_gid被关闭,那么php脚本能够对文件进行访问,而且相同组的用户也能够对文件进行访问。而且相同组的用户也能够对文件进行访问。

建议设置为:

safe_mode_gid = off

如果不进行设置,可能我们无法对我们服务器网站目录下的文件进行操作了,比如我们需要对文件进行操作的时候。

(3)安全模式下执行程序主目录

如果安全模式打开了,但是却是要执行某些程序的时候,可以指定要执行程序的主目录:

safe_mode_exec_dir = /usr/bin

一般情况下是不需要执行什么程序的,所以推荐不要执行系统程序目录,可以指向一个目录:然后把需要执行的程序拷贝过去,比如:

safe_mode_exec_dir = /temp/cmd

但是,我更推荐不要执行任何程序,那么就可以指向我们网页目录:

safe_mode_exec_dir = /usr/www

(4)安全模式下包含文件

如果要在安全模式下包含某些公共文件,那么就修改一下选项:

safe_mode_include_dir = /usr/www/include/

其实一般php脚本中包含文件都是在程序自己已经写好了,这个可以根据具体需要设置。

(5)控制php脚本能访问的目录

使用open_basedir选项能够控制PHP脚本只能访问指定的目录,这样能够避免PHP脚本访问不应该访问的文件,一定程序上显示了phpshell的危害,我们一般可以设置为只能访问网站目录:

open_basedir = /usr/www

(6)关闭危险函数

如果打开了安全模式,那么函数禁止是可以不需要的,但是我们为了安全还是考虑进去。比如,我们觉得不希望执行包括system()等在内的执行明了的php函数,或者能够查看php信息的phpinfo()等函数,那么我们就可以禁止它们:

disable_functions = system, passthru, exec, shell_exec, popen, phpinfo, escapeshellarg, escapeshellcmd, proc_close, proc_open, dl

如果你要禁止任何文件和目录的操作,那么可以关闭很多文件操作

disable_functions = chdir, chroot, dir, getcwd, opendir, readdir, scandir, fopen, unlink, delete, copy, mkdir, rmdir, rename, file, file_get_contents, fputs, fwrite, chgrp,chmod, chown

以上只是列了部分比较常用的文件处理函数,你也可以把上面执行命令函数和这个函数结合,就能给抵制大部分的phpshell了。

(7)关闭php版本信息在http头中的泄露

我们为了防止黑客获取服务器中php版本的信息,可以关闭该信息泄露在http头中:

expose_php = off

比如黑客在 telnet www.girlcoding.com:80 的时候,那么将无法看到PHP的信息

(8)关闭注册全局变量

在PHP中提交的变量,包括使用POST或者GET提交的变量,都将自动注册为全局变量,能够直接访问,这是对服务器非常不安全的,所以我们不能让它注册为全局变量,就把注册全局变量选项关闭:

register_globals = off

当然,如果这样设置了,那么获取对应变量的时候就要采取合理方式,比如获取GET提交的变量var,那么就要用$_GET['var']来进行获取,这个php程序员要注意。

(9)打开magic_quotes_gpc来防止SQL注入

SQL注入是非常危险的问题,小则网站后台被入侵,重则整个服务器沦陷,所以一定要小心。php.ini中有一个设置:

magic_quotes_gpc = off

这个默认是关闭的,如果它打开后将自动把用户提交对sql的查询进行转换,比如把’转为’等,这对防止sql注入有很大作用,所以我们推荐设置为:

magic_quotes_gpc = off

有次程序在本地的上传不好用了,但在服务器上好使~也可能是继承了discuz的核心文件造成的,获取路径出现了问题,后来打开这个参数,问题解决了。

(10)错误信息控制

一般php在没有连接到数据库或者其他情况下会有错误提示,一般错误信息中会包含php脚本当前的路径信息或者查询的SQL语句等信息,这类信息提供给黑客后,是不安全的,所以一般服务器建议禁止错误提示:

display_errors = Off

如果你确实要显示错误信息,一定要设置显示错误的级别,比如只显示警告以上的信息:

error_reporting = E_WARNING & E_ERROR

当然,我还是建议关闭错误提示。

(11)错误日志

建议在关闭display_errors后能够把是错误信息记录下来,便于查找服务器运行的原因:

log_errors = On

同时也要设置错误日志存放的目录,建议根apache的日志存在一起:

error_log = /usr/local/apache2/logs/php_error.log

注意:给文件必须允许apache用户或组具有写的权限。

大家都知道使用$_REQUEST可以直接省去了判断post,get一些代码,使用起来更简单,但是如果要详细的去想我们会觉得$_REQUEST太可怕了。下面看分析。

我们都知道,处理表单数据,可以使用PHP的$_GET和$_POST这两个超全局变量,具体是哪个由form表单提交时的method指定。除此之外PHP还为我们提供了$_REQUEST数组。但是其不仅包含$_GET和$_POST的所有数据元素,同时其还会包含$_COOKIE这个超全局数组的所有数据元素。
可是大家有没有想过,如果这三个数组里面的key相同,那么我用$_REQUEST得到的到底是哪个数组的值呢?会不会有什么问题?
我用如下代码为大家做演示,因为只是想说明问题,所以这里面不对$_COOKIE进行设置,请大家自行处理:

 代码如下 复制代码
<?php
       var_dump($_GET['a'],$_POST['a'],$_REQUEST['a']);
?>
<html>
<head><title>demo</title></head>
<body>
        <form method= 'post' action = 'req.php?a=xxx'>
                <input type='hidden' name='a' value='yyy'/>
                <input type='submit' name='submit' value='submit'/>
        </form>
</body>
</html>

当我提交表单的时候,我获取的页面内容为:

string(3) "xxx" string(3) "yyy" string(3) "yyy"

同样的内容,在$_REQUEST里面,POST的值覆盖了GET的值,这到底是怎么回事呢?
其实这是在PHP的配置文件里面设置的,让我们来看一下php.ini这个配置文件,在第466行左右有如下内容:

 代码如下 复制代码

; This directive describes the order in which PHP registers GET, POST, Cookie,
; Environment and Built-in variables (G, P, C, E & S respectively, often
; referred to as EGPCS or GPC).  Registration is done from left to right, newer
; values override older values.
variables_order = "EGPCS"

这个EGPCS就是说明用$_REQUEST数组获取内容的优先级,其字母的含义分别代表为:E代表$_ENV,G代表$_GET,P代表$_POST,C代表$_COOKIE,S代表$_SESSION。后面出现的数据会覆盖前面写入的数据,其默认的数据写入方式就是EGPCS,所以POST包含的数据将覆盖GET中使用相同关键字的数据。


$_REQUEST[]具用$_POST[] $_GET[]的功能,但是$_REQUEST[]比较慢。通过post和get方法提交的所有数据都可以通过$_REQUEST数组获得

文章分析了关于PHP5.2.X防止Hash冲突拒绝服务攻击的Patch方法,有需要了朋友可参考一下。


上周的时候Dmitry突然在5.4发布在即的时候, 引入了一个新的配置项:

Added max_input_vars directive to prevent attacks based on hash collision这个预防的攻击, 就是”通过调用Hash冲突实现各种语言的拒绝服务攻击漏洞”(multiple implementations denial-of-service via hash algorithm collision).

攻击的原理很简单, 目前很多语言, 使用hash来存储k-v数据, 包括常用的来自用户的POST数据, 攻击者可以通过构造请求头, 并伴随POST大量的特殊的”k”值(根据每个语言的Hash算法不同而定制), 使得语言底层保存POST数据的Hash表因为”冲突”(碰撞)而退化成链表.


这样一来, 如果数据量足够大, 那么就可以使得语言在计算, 查找, 插入的时候, 造成大量的CPU占用, 从而实现拒绝服务攻击.

PHP5.4是通过增加一个限制来尽量避免被此类攻击影响:

  - max_input_vars - specifies how many GET/POST/COOKIE input variables may be    accepted. default value 1000

 

大家如果有用5.2的, 如果被此类攻击威胁, 可以打上下面的patch, PHP5.3的, 可以考虑升级到5.3.9, 已经包含了此patch(因为5.3.9目前是RC状态, 所以如果不愿意升级, 也可以参照这个patch自己为5.3写一个):

 

防止办法

1. Cd into the PHP src run: patch -p1 < php-5.2.*-max-input-vars.patch
2. Since the latest PHP 5.3.9-RC4 has fixed this issue, so for 5.3 you can upgrade to 5.3.9RC4
   Of course if you don't want to upgrade to a RC version, you can simply tweak this patch into a 5.3 suitable patch.


大家可到https://github.com/laruence/laruence.github.com/tree/master/php-5.2-max-input-vars下载包

还记得我之前说的PHP Hash Collisions Ddos漏洞吧? 最初的时候, 开发组给出的修复方案, 采用的是如果超过max_input_vars, 就报错(E_ERROR), 继而导致PHP出错结束. 而后来, 为了更加轻量级的解决这个问题, 我们又改善了一下, 变成了如果超过max_input_vars, 就发出警告(E_WARNING), 并且不再往目的数组添加, 但是流程继续. 然后我们发布了5.3.9.

这个新的修复方法初衷是好的, 但是却带来一个严重的问题(5.3.10中已经修复), 这个问题最初是由Stefan Esser发现的. 请看之前(5.3.9)最终的修复方案(php_register_variable_ex):

 代码如下 复制代码

while (1) {

     if (zend_symtable_find(symtable1, escaped_index, index_len + 1, (void **) &gpc_element_p) == FAILURE

          || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { //(3)

          if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { // (4)

               if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) {

                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. ...", PG(max_input_vars)); // (1)

               }

               MAKE_STD_ZVAL(gpc_element);

               array_init(gpc_element);

               zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p);

          }

          //......

     }

     //.....

     symtable1 = Z_ARRVAL_PP(gpc_element_p); // (2)

     goto plain;

}< li>


注意到, 如果此时注册一个数组变量(在GET中类似于: a[]=2), 并且此时这个变量刚好是第max_input_vars个变量的时候, 会触发一个警告(1), 此时一切正常.

但是, 如果此时还是注册一个数组变量,但是这个变量已经是第max_input_vars + 1个变量的时候, 那么此时gpc_element_p将成为一个未初始化的指针, 而因为现在逻辑会继续走, 也就会走到(2)号位置, 导致解引用了一个未初始化的指针. 于是, Boomb~

那么, 到目前位置, 我们就可以使用这样的特性来对5.3.9做Ddos了. 如果Server开启了Core Dump的话, 这个效果会非常明显.

然而, 这个问题还会导致一个更严重的问题:

还是上面的代码, 在最外层有一个循环, 这个循环起作用的时刻在注册类似于a[b]=2的pair对的时候, 循环将会执行俩次, 第一次插入a[], 第二次往a[]中插入b. 然后再让我们注意下(3), 如果在目的数组中找不到一个想要的元素, **或者这个元素不为数组**, 则也会直接导致流程留到(2), 于是问题就出现了.

对于这样的POST串(默认max_input_vars是1000):

 1=1&1=2&..........&999=1&x="我是恶意的string"&x[0]=


会发生什么事情呢?

让我来一步一步描述下:

1. 从1到999没什么问题, 都被正常插入

2. x是1000个元素, 所以触发警告, 也没有问题, x被插入

3. x[0]插入的时候, (3)号语句判断发现不是Arrary于是进入if体, 但是此时(4)号语句失败, 于是流程最终流到了(2)

4. 此时, gpc_element_p指向x, 也就是那个我们伪造的字符串….

现在让我们看看关键的数据结构, zval:

 代码如下 复制代码

struct _zval_struct {

    /* Variable information */

    zvalue_value value; /* value */

    zend_uint refcount__gc;

    zend_uchar type; /* active type */

    zend_uchar is_ref__gc;

};< li>


然后看zvalue_value:

 代码如下 复制代码

typedef union _zvalue_value {

    long lval; /* long value */

    double dval; /* double value */

    struct {

        char *val;

        int len;

    } str;

    HashTable *ht; /* hash table value */

    zend_object_value obj;

} zvalue_value;< li>


zvalue_value是一个联合体, 于是我们构造的字符串区域的内存, 就会被当做一个Hashtable结构体:

 代码如下 复制代码

typedef struct _hashtable {

    uint nTableSize;

    uint nTableMask;

    uint nNumOfElements;

    ulong nNextFreeElement;

    Bucket *pInternalPointer; /* Used for element traversal */

    Bucket *pListHead;

    Bucket *pListTail;

    Bucket **arBuckets;

    dtor_func_t pDestructor; //注意这个

    zend_bool persistent;

    unsigned char nApplyCount;

    zend_bool bApplyProtection;

#if ZEND_DEBUG

    int inconsistent;

#endif

} HashTable;< li>


在Hashtable结构体中, 有一个pDestructor, 这个指针指向一个函数, 当这个Hashtable中有元素要被清除的时候, 就会调用它…

也就是说, 你可以随心所欲的设置一个地址(pDestructor), 然后让PHP去调用它(诱使一个元素被删除).

[!--infotagslink--]

相关文章

  • php漏洞之跨网站请求伪造与防止伪造方法

    今天我来给大家介绍在php中跨网站请求伪造的实现方法与最后我们些常用的防止伪造的具体操作方法,有需要了解的朋友可进入参考。 伪造跨站请求介绍 伪造跨站请求...2016-11-25
  • php语言实现redis的客户端

    php语言实现redis的客户端与服务端有一些区别了因为前面介绍过服务端了这里我们来介绍客户端吧,希望文章对各位有帮助。 为了更好的了解redis协议,我们用php来实现...2016-11-25
  • jQuery+jRange实现滑动选取数值范围特效

    有时我们在页面上需要选择数值范围,如购物时选取价格区间,购买主机时自主选取CPU,内存大小配置等,使用直观的滑块条直接选取想要的数值大小即可,无需手动输入数值,操作简单又方便。HTML首先载入jQuery库文件以及jRange相关...2015-03-15
  • 不打开网页直接查看网站的源代码

      有一种方法,可以不打开网站而直接查看到这个网站的源代码..   这样可以有效地防止误入恶意网站...   在浏览器地址栏输入:   view-source:http://...2016-09-20
  • 如何获取网站icon有哪些可行的方法

    获取网站icon,常用最简单的方法就是通过website/favicon.ico来获取,不过由于很多网站都是在页面里面设置favicon,所以此方法很多情况都不可用。 更好的办法是通过google提供的服务来实现:http://www.google.com/s2/favi...2014-06-07
  • php 调用goolge地图代码

    <?php require('path.inc.php'); header('content-Type: text/html; charset=utf-8'); $borough_id = intval($_GET['id']); if(!$borough_id){ echo ' ...2016-11-25
  • JS基于Mootools实现的个性菜单效果代码

    本文实例讲述了JS基于Mootools实现的个性菜单效果代码。分享给大家供大家参考,具体如下:这里演示基于Mootools做的带动画的垂直型菜单,是一个初学者写的,用来学习Mootools的使用有帮助,下载时请注意要将外部引用的mootools...2015-10-23
  • JS实现的简洁纵向滑动菜单(滑动门)效果

    本文实例讲述了JS实现的简洁纵向滑动菜单(滑动门)效果。分享给大家供大家参考,具体如下:这是一款纵向布局的CSS+JavaScript滑动门代码,相当简洁的手法来实现,如果对颜色不满意,你可以试着自己修改CSS代码,这个滑动门将每一...2015-10-21
  • JS+CSS实现分类动态选择及移动功能效果代码

    本文实例讲述了JS+CSS实现分类动态选择及移动功能效果代码。分享给大家供大家参考,具体如下:这是一个类似选项卡功能的选择插件,与普通的TAb区别是加入了动画效果,多用于商品类网站,用作商品分类功能,不过其它网站也可以用,...2015-10-21
  • JS实现自定义简单网页软键盘效果代码

    本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
  • php 取除连续空格与换行代码

    php 取除连续空格与换行代码,这些我们都用到str_replace与正则函数 第一种: $content=str_replace("n","",$content); echo $content; 第二种: $content=preg_replac...2016-11-25
  • php简单用户登陆程序代码

    php简单用户登陆程序代码 这些教程很对初学者来讲是很有用的哦,这款就下面这一点点代码了哦。 <center> <p>&nbsp;</p> <p>&nbsp;</p> <form name="form1...2016-11-25
  • PHP实现清除wordpress里恶意代码

    公司一些wordpress网站由于下载的插件存在恶意代码,导致整个服务器所有网站PHP文件都存在恶意代码,就写了个简单的脚本清除。恶意代码示例...2015-10-23
  • JS实现双击屏幕滚动效果代码

    本文实例讲述了JS实现双击屏幕滚动效果代码。分享给大家供大家参考,具体如下:这里演示双击滚屏效果代码的实现方法,不知道有觉得有用处的没,现在网上还有很多还在用这个特效的呢,代码分享给大家吧。运行效果截图如下:在线演...2015-10-30
  • js识别uc浏览器的代码

    其实挺简单的就是if(navigator.userAgent.indexOf('UCBrowser') > -1) {alert("uc浏览器");}else{//不是uc浏览器执行的操作}如果想测试某个浏览器的特征可以通过如下方法获取JS获取浏览器信息 浏览器代码名称:navigator...2015-11-08
  • jQuery+slidereveal实现的面板滑动侧边展出效果

    我们借助一款jQuery插件:slidereveal.js,可以使用它控制面板左右侧滑出与隐藏等效果,项目地址:https://github.com/nnattawat/slideReveal。如何使用首先在页面中加载jquery库文件和slidereveal.js插件。复制代码 代码如...2015-03-15
  • JS日期加减,日期运算代码

    一、日期减去天数等于第二个日期function cc(dd,dadd){//可以加上错误处理var a = new Date(dd)a = a.valueOf()a = a - dadd * 24 * 60 * 60 * 1000a = new Date(a)alert(a.getFullYear() + "年" + (a.getMonth() +...2015-11-08
  • PHP+jQuery翻板抽奖功能实现

    翻板抽奖的实现流程:前端页面提供6个方块,用数字1-6依次表示6个不同的方块,当抽奖者点击6个方块中的某一块时,方块翻转到背面,显示抽奖中奖信息。看似简单的一个操作过程,却包含着WEB技术的很多知识面,所以本文的读者应该熟...2015-10-21
  • PHP开发微信支付的代码分享

    微信支付,即便交了保证金,你还是处理测试阶段,不能正式发布。必须到你通过程序测试提交订单、发货通知等数据到微信的系统中,才能申请发布。然后,因为在微信中是通过JS方式调用API,必须在微信后台设置支付授权目录,而且要到...2014-05-31
  • mac下Apache + MySql + PHP搭建网站开发环境

    首先为什不自己分别搭建Apache,PHP和MySql的环境呢?这样自己可以了解更多知识,说起来也更酷。可也许因为我懒吧,我是那种“既然有现成的,用就是了”的人。君子生非异也,善假于物也。两千年前的荀子就教导我们,要善于利用工具...2014-06-07