图片 2

机制详解,安全编程建议

PHP在魔术函数__autoload()方法出现以前,如果你要在一个程序文件中实例化100个对象,那么你必须用include或者require包含进来100个类文件,或者你把这100个类定义在同一个类文件中——相信这个文件一定会非常大。但是__autoload()方法出来了,以后就不必为此大伤脑筋了,这个类会在你实例化对象之前自动加载制定的文件。

简介

要提供互联网服务,当你在开发代码的时候必须时刻保持安全意识。可能大部分
PHP 脚本都对安全问题都不在意,这很大程度上是因为有大量的无经验程序员在使用这门语言。但是,没有理由让你因为对你的代码的不确定性而导致不一致的安全策略。当你在服务器上放任何涉及到钱的东西时,就有可能会有人尝试破解它。创建一个论坛程序或者任何形式的购物车,被攻击的可能性就上升到了无穷大。

图片 1

PHP的基本GC概念

PHP语言同其他语言一样,具有垃圾回收机制。那么今天我们要为大家讲解的内容就是关于PHP垃圾回收机制的相关问题。希望对大家有所帮助。

PHP strtotime应用经验之谈PHP memory_get_usage()管理内存PHP
unset全局变量运用问题详解PHP
unset()函数销毁变量教你快速实现PHP全站权限验证一、PHP
垃圾回收机制(Garbage Collector 简称GC)
在PHP中,没有任何变量指向这个对象时,这个对象就成为垃圾。PHP会将其在内存中销毁;这是PHP的GC垃圾处理机制,防止内存溢出。当一个PHP线程结束时,当前占用的所有内存空间都会被销毁,当前程序中所有对象同时被销毁。GC进程一般都跟着每起一个SESSION而开始运行的.gc目的是为了在session文件过期以后自动销毁删除这些文件.二、__destruct
/unset __destruct() 析构函数,是在垃圾对象被回收时执行。

unset 销毁的是指向对象的变量,而不是这个对象。三、 Session
与PHP垃圾回收机制由于PHP的工作机制,它并没有一个daemon线程来定期的扫描Session信息并判断其是否失效,当一个有效的请求发生时,PHP
会根据全局变量
session.gc_probability和session.gc_divisor的值,来决定是否启用一个GC。
在默认情况下,session.gc_probability=1, session.gc_divisor
=100也就是说有1%的可能性启动GC(也就是说100个请求中只有一个gc会伴随100个中的某个请求而启动).

PHP垃圾回收机制的工作就是扫描所有的Session信息,用当前时间减去session最后修改的时间,同session.gc_maxlifetime参数进行比较,如果生存时间超过gc_maxlifetime(默认24分钟),就将该session删除。

但是,如果你Web服务器有多个站点,多个站点时,GC处理session可能会出现意想不到的结果,原因就是:GC在工作时,并不会区分不同站点的session.那么这个时候怎么解决呢?

  • 修改session.save_path,或使用session_save_path()让每个站点的session保存到一个专用目录,
  • 提供GC的启动率,自然,PHP垃圾回收机制的启动率提高,系统的性能也会相应减低,不推荐。
  • 在代码中判断当前session的生存时间,利用session_destroy()删除。

1. autoload 机制概述

在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利。这也是OO设计的基本思想之一。在PHP5之前,如果需要使用一个类,只需要直接使用include/require将其包含进来即可。下面是一个实际的例子:

/* Person.class.php */
<?php
 class Person {
  var $name, $age;

  function __construct ($name, $age)
  {
   $this->name = $name;
   $this->age = $age;
  }
 }
?>

/* no_autoload.php */
<?php
 require_once (”Person.class.php”);

 $person = new Person(”Altair”, 6);
 var_dump ($person);
?>

在这个例子中,no-autoload.php文件需要使用Person类,它使用了require_once将其包含,然后就可以直接使用Person类来实例化一个对象。

但随着项目规模的不断扩大,使用这种方式会带来一些隐含的问题:如果一个PHP文件需要使用很多其它类,那么就需要很多的require/include语句,这样有可能会造成遗漏或者包含进不必要的类文件。如果大量的文件都需要使用其它的类,那么要保证每个文件都包含正确的类文件肯定是一个噩梦。

PHP5为这个问题提供了一个解决方案,这就是类的自动装载(autoload)机制。autoload机制可以使得PHP程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件include进来,这种机制也称为lazy
loading。

下面是使用autoload机制加载Person类的例子:

/* autoload.php */
<?php
 function __autoload($classname)
{
  $classpath="./".$classname.'.class.php';
  if(file_exists($classpath))
  {
    require_once($classpath);
  }
  else
  {
    echo 'class file'.$classpath.'not found!';
   }
}

 $person = new Person(”Altair”, 6);
 var_dump ($person);
 ?>

通常PHP5在使用一个类时,如果发现这个类没有加载,就会自动运行__autoload()函数,在这个函数中我们可以加载需要使用的类。在我们这个简单的例子中,我们直接将类名加上扩展名”.class.php”构成了类文件名,然后使用require_once将其加载。从这个例子中,我们可以看出autoload至少要做三件事情,第一件事是根据类名确定类文件名,第二件事是确定类文件所在的磁盘路径(在我们的例子是最简单的情况,类与调用它们的PHP程序文件在同一个文件夹下),第三件事是将类从磁盘文件中加载到系统中。第三步最简单,只需要使用include/require即可。要实现第一步,第二步的功能,必须在开发时约定类名与磁盘文件的映射方法,只有这样我们才能根据类名找到它对应的磁盘文件。

因此,当有大量的类文件要包含的时候,我们只要确定相应的规则,然后在__autoload()函数中,将类名与实际的磁盘文件对应起来,就可以实现lazy
loading的效果。从这里我们也可以看出__autoload()函数的实现中最重要的是类名与实际的磁盘文件映射规则的实现。

但现在问题来了,如果在一个系统的实现中,如果需要使用很多其它的类库,这些类库可能是由不同的开发人员编写的,其类名与实际的磁盘文件的映射规则不尽相同。这时如果要实现类库文件的自动加载,就必须在__autoload()函数中将所有的映射规则全部实现,这样的话__autoload()函数有可能会非常复杂,甚至无法实现。最后可能会导致__autoload()函数十分臃肿,这时即便能够实现,也会给将来的维护和系统效率带来很大的负面影响。在这种情况下,难道就没有更简单清晰的解决办法了吧?答案当然是:NO!
在看进一步的解决方法之前,我们先来看一下PHP中的autoload机制是如何实现的。

背景

为了确保你的 web 内容安全,这里有一些常规的安全准则:

引用计数基本知识

每个php变量存在一个叫做”zval”的变量容器中.一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息.

第一个是”is_ref”,是个bool值,用来标识这个变量是否是属于引用集合(reference
set).通过这个字节,php引擎才能把普通变量和引用变量区分开.由于php允许用户通过使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制,来优化内存使用.第二个额外字节是”refcount”,用来表示指向这个zval变量容器的变量(也称符号即symbol)个数.

当一个变量被赋常量值时,就会生成一个zval变量容器,如下例所示:

<?php 
  $a = "new string"; 
  ?>

在上例中,新的变量是a,是在当前作用域中生成的.并且生成了类型为string和值为”new
string”的变量容器.在额外的两个字节信息中,”is_ref”被默认设置为false,因为没有任何自定义的引用生成.”refcount”被设定为1,因为这里只有一个变量使用这个变量容器.调用xdebug查看一下变量内容:

<?php 
  $a = "new string"; 
  xdebug_debug_zval('a'); 
  ?>

以上代码会输出:

a: (refcount=1, is_ref=0)='new string'

对变量a增加一个引用计数

<?php 
  $a = "new string"; 
  $b = $a; 
  xdebug_debug_zval('a'); 
  ?>

以上代码会输出:

a: (refcount=2, is_ref=0)='new string'

这时,引用次数是2,因为同一变量容器被变量a和变量b关联.当没必要时,php不会去复制已生成的变量容器.变量容器在”refcount”变成0时就被销毁.当任何关联到某个变量容易的变量离开它的作用域(比如:函数执行结束),或者对变量调用了unset()函数,”refcount”就会减1,下面例子就能说明:

<?php 
  $a = "new string"; 
  $b = $c = $a; 
  xdebug_debug_zval('a'); 
  unset($b, $c); 
  xdebug_debug_zval('a'); 
  ?>

以上代码会输出:

a: (refcount=3, is_ref=0)='new string' a: (refcount=1, is_ref=0)='new string'

如果我们现在执行unset($a),$包含的类型和值的这个容器就会从内存删除

2. PHP 的 autoload 机制的实现

我们知道,PHP文件的执行分为两个独立的过程,第一步是将PHP文件编译成普通称之为OPCODE的字节码序列(实际上是编译成一个叫做zend_op_array的字节数组),第二步是由一个虚拟机来执行这些OPCODE。PHP的所有行为都是由这些OPCODE来实现的。因此,为了研究PHP中autoload的实现机制,我们将autoload.php文件编译成opcode,然后根据这些OPCODE来研究PHP在这过程中都做了些什么:

/* autoload.php 编译后的OPCODE列表,是使用作者开发的OPDUMP工具
     * 生成的结果,可以到网站 http://www.phpinternals.com/ 下载该软件。
     */
    1: <?php
    2:  // require_once (”Person.php”);
    3:  
    4:  function __autoload ($classname) {
            0  NOP                
            0  RECV                1
    5:   if (!class_exists($classname)) {
            1  SEND_VAR            !0
            2  DO_FCALL            ‘class_exists’ [extval:1]
            3  BOOL_NOT            $0 =>RES[~1]     
            4  JMPZ                ~1, ->8
    6:    require_once ($classname. “.class.php”);
            5  CONCAT              !0, ‘.class.php’ =>RES[~2]     
            6  INCLUDE_OR_EVAL     ~2, REQUIRE_ONCE
    7:   }
            7  JMP                 ->8
    8:  }
            8  RETURN              null
    9:  
   10:  $p = new Person(’Fred’, 35);
            1  FETCH_CLASS         ‘Person’ =>RES[:0]     
            2  NEW                 :0 =>RES[$1]     
            3  SEND_VAL            ‘Fred’
            4  SEND_VAL            35
            5  DO_FCALL_BY_NAME     [extval:2]
            6  ASSIGN              !0, $1
   11:  
   12:  var_dump ($p);
            7  SEND_VAR            !0
            8  DO_FCALL            ‘var_dump’ [extval:1]
   13: ?>

在autoload.php的第10行代码中我们需要为类Person实例化一个对象。因此autoload机制一定会在该行编译后的opcode中有所体现。从上面的第10行代码生成的OPCODE中我们知道,在实例化对象Person时,首先要执行FETCH_CLASS指令。我们就从PHP对FETCH_CLASS指令的处理过程开始我们的探索之旅。

通过查阅PHP的源代码(我使用的是PHP 5.3alpha2版本)可以发现如下的调用序列:

ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, …) (zend_vm_def.h 1864行)
 => zend_fetch_class (zend_execute_API.c 1434行)
  =>zend_lookup_class_ex (zend_execute_API.c 964行)
   => zend_call_function(&fcall_info, &fcall_cache) (zend_execute_API.c 1040行)

在最后一步的调用之前,我们先看一下调用时的关键参数:

/* 设置autoload_function变量值为”__autoload” */
 fcall_info.function_name = &autoload_function;  // Ooops, 终于发现”__autoload”了
 …
 fcall_cache.function_handler = EG(autoload_func); // autoload_func !

zend_call_function是Zend
Engine中最重要的函数之一,其主要功能是执行用户在PHP程序中自定义的函数或者PHP本身的库函数。zend_call_function有两个重要的指针形参数fcall_info,
fcall_cache,它们分别指向两个重要的结构,一个是zend_fcall_info,
另一个是zend_fcall_info_cache。zend_call_function主要工作流程如下:如果fcall_cache.function_handler指针为NULL,则尝试查找函数名为fcall_info.function_name的函数,如果存在的话,则执行之;如果fcall_cache.function_handler不为NULL,则直接执行fcall_cache.function_handler指向的函数。

现在我们清楚了,PHP在实例化一个对象时(实际上在实现接口,使用类常数或类中的静态变量,调用类中的静态方法时都会如此),首先会在系统中查找该类(或接口)是否存在,如果不存在的话就尝试使用autoload机制来加载该类。而autoload机制的主要执行过程为:

  1. 检查执行器全局变量函数指针autoload_func是否为NULL。
  2. 如果autoload_func==NULL,
    则查找系统中是否定义有__autoload()函数,如果没有,则报告错误并退出。
  3. 如果定义了__autoload()函数,则执行__autoload()尝试加载类,并返回加载结果。
  4. 如果autoload_func不为NULL,则直接执行autoload_func指针指向的函数用来加载类。注意此时并不检查__autoload()函数是否定义。

真相终于大白,PHP提供了两种方法来实现自动装载机制,一种我们前面已经提到过,是使用用户定义的__autoload()函数,这通常在PHP源程序中来实现;另外一种就是设计一个函数,将autoload_func指针指向它,这通常使用C语言在PHP扩展中实现。如果既实现了__autoload()函数,又实现了autoload_func(将autoload_func指向某一PHP函数),那么只执行autoload_func函数。

别相信表单

攻击表单很简单。通过使用一个简单的 JavaScript
技巧,你可以限制你的表单只允许在评分域中填写 1 到 5
的数字。如果有人关闭了他们浏览器的 JavaScript
功能或者提交自定义的表单数据,你客户端的验证就失败了。

用户主要通过表单参数和你的脚本交互,因此他们是最大的安全风险。你应该学到什么呢?在
PHP 脚本中,总是要验证 传递给任何 PHP
脚本的数据。在本文中,我们向你演示了如何分析和防范跨站脚本(XSS)攻击,它可能会劫持用户凭据(甚至更严重)。你也会看到如何防止会玷污或毁坏你数据的
MySQL 注入攻击。

复合类型(compound types)

当考虑像array和object这样的复合类型时,事情会稍微有些复杂.与标量(scalar)类型的值不同,array和object类型的变量把它们的成员或属性存在自己的符号表中.这意味着下面的例子将生成三个zval变量容器

<?php 
      $a = array('meaning' => 'life', 'number' => 42); 
      xdebug_debug_zval('a'); 
  ?>

以上代码输出:

a: (refcount=1, is_ref=0)=array ('meaning' => (refcount=1, is_ref=0)='life', 'number' => (refcount=1, is_ref=0)=42)

这三个zval变量容器是:a,meaning,number.增加和减少refcount的规则和上面提到的一样特例,添加数组本身作为数组元素时:

<?php 
  $a = array('one'); 

  $a[] = &$a; 

  xdebug_debug_zval('a'); 
  ?>

以上代码输出的结果:

a: (refcount=2, is_ref=1)=array (0 => (refcount=1, is_ref=0)='one', 1 => (refcount=2, is_ref=1)=...)

可以看到数组a和数组本身元素a[1]指向的变量容器refcount为2

当对数组$a调用unset函数时,$a的refcount变为1,发生了内存泄漏
清理变量容器的问题。

尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素”1″仍然指向数组本身,所以这个容器不能被消除.因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏.庆幸的是,php将在请求结束时清除这个数据结构,但是php清除前,将耗费不少内存空间。

3. SPL autoload 机制的实现

SPL是Standard PHP
Library(标准PHP库)的缩写。它是PHP5引入的一个扩展库,其主要功能包括autoload机制的实现及包括各种Iterator接口或类。SPL
autoload机制的实现是通过将函数指针autoload_func指向自己实现的具有自动装载功能的函数来实现的。SPL有两个不同的函数spl_autoload,
spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的自动加载机制。

spl_autoload是SPL实现的默认的自动加载函数,它的功能比较简单。它可以接收两个参数,第一个参数是$class_name,表示类名,第二个参数$file_extensions是可选的,表示类文件的扩展名,可以在$file_extensions中指定多个扩展名,护展名之间用分号隔开即可;如果不指定的话,它将使用默认的扩展名.inc或.php。spl_autoload首先将$class_name变为小写,然后在所有的include
path中搜索$class_name.inc或$class_name.php文件(如果不指定$file_extensions参数的话),如果找到,就加载该类文件。你可以手动使用spl_autoload(”Person”,
“.class.php”)来加载Person类。实际上,它跟require/include差不多,不同的它可以指定多个扩展名。

怎样让spl_autoload自动起作用呢,也就是将autoload_func指向spl_autoload?答案是使用spl_autoload_register函数。在PHP脚本中第一次调用spl_autoload_register()时不使用任何参数,就可以将autoload_func指向spl_autoload。

通过上面的说明我们知道,spl_autoload的功能比较简单,而且它是在SPL扩展中实现的,我们无法扩充它的功能。如果想实现自己的更灵活的自动加载机制怎么办呢?这时,spl_autoload_call函数闪亮登场了。

我们先看一下spl_autoload_call的实现有何奇妙之处。在SPL模块内部,有一个全局变量autoload_functions,它本质上是一个HashTable,不过我们可以将其简单的看作一个链表,链表中的每一个元素都是一个函数指针,指向一个具有自动加载类功能的函数。spl_autoload_call本身的实现很简单,只是简单的按顺序执行这个链表中每个函数,在每个函数执行完成后都判断一次需要的类是否已经加载,如果加载成功就直接返回,不再继续执行链表中的其它函数。如果这个链表中所有的函数都执行完成后类还没有加载,spl_autoload_call就直接退出,并不向用户报告错误。因此,使用了autoload机制,并不能保证类就一定能正确的自动加载,关键还是要看你的自动加载函数如何实现。

那么自动加载函数链表autoload_functions是谁来维护呢?就是前面提到的spl_autoload_register函数。它可以将用户定义的自动加载函数注册到这个链表中,并将autoload_func函数指针指向spl_autoload_call函数(注意有一种情况例外,具体是哪种情况留给大家思考)。我们也可以通过spl_autoload_unregister函数将已经注册的函数从autoload_functions链表中删除。

上节说过,当autoload_func指针非空时,就不会自动执行__autoload()函数了,现在autoload_func已经指向了spl_autoload_call,如果我们还想让__autoload()函数起作用应该怎么办呢?当然还是使用spl_autoload_register(__autoload)调用将它注册到autoload_functions链表中。

现在回到第一节最后的问题,我们有了解决方案:根据每个类库不同的命名机制实现各自的自动加载函数,然后使用spl_autoload_register分别将其注册到SPL自动加载函数队列中就可了。这样我们就不用维护一个非常复杂的__autoload函数了。

别相信用户

假定你网站获取的每一份数据都充满了有害的代码。清理每一部分,即便你相信没有人会尝试攻击你的站点。

回收周期

5.3.0PHP使用了新的同步周期回收算法,来处理上面所说的内存泄漏问题

首先,我们先要建立一些基本规则:

如果一个引用计数增加,它将继续被使用,当然就不再垃圾中.如果引用技术减少到零,所在的变量容器将被清除(free).就是说,仅仅在引用计数减少到非零值时,才会产生垃圾周期(grabage
cycle).其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。

图片 2

为避免不得不检查所有引用计数可能减少的垃圾周期,这个算法把所有可能根(possible
roots 都是zval变量容器),放在根缓冲区(root
buffer)中(用紫色标记),这样可以同时确保每个可能的垃圾根(possible garbage
root)在缓冲区只出现一次.仅仅在根缓冲区满了时,才对缓冲区内部所有不同的变量容器执行垃圾回收操作。

4. autoload 效率问题及对策

使用autoload机制时,很多人的第一反应就是使用autoload会降低系统效率,甚至有人干脆提议为了效率不要使用autoload。在我们了解了autoload实现的原理后,我们知道autoload机制本身并不是影响系统效率的原因,甚至它还有可能提高系统效率,因为它不会将不需要的类加载到系统中。

那么为什么很多人都有一个使用autoload会降低系统效率的印象呢?实际上,影响autoload机制效率本身恰恰是用户设计的自动加载函数。如果它不能高效的将类名与实际的磁盘文件(注意,这里指实际的磁盘文件,而不仅仅是文件名)对应起来,系统将不得不做大量的文件是否存在(需要在每个include
path中包含的路径中去寻找)的判断,而判断文件是否存在需要做磁盘I/O操作,众所周知磁盘I/O操作的效率很低,因此这才是使得autoload机制效率降低的罪魁祸首!

因此,我们在系统设计时,需要定义一套清晰的将类名与实际磁盘文件映射的机制。这个规则越简单越明确,autoload机制的效率就越高。

结论:autoload机制并不是天然的效率低下,只有滥用autoload,设计不好的自动装载函数才会导致其效率的降低。

关闭全局变量

你可能会有的最大安全漏洞是启用了 register_globals
配置参数。幸运的是,PHP 4.2 及以后版本默认关闭了这个配置。如果打开了
register_globals,你可以在你的 php.ini 文件中通过改变
register_globals 变量为 Off 关闭该功能:

register_globals = Off

新手程序员觉得注册全局变量很方便,但他们不会意识到这个设置有多么危险。一个启用了全局变量的服务器会自动为全局变量赋任何形式的参数。为了了解它如何工作以及为什么有危险,让我们来看一个例子。

假设你有一个称为 process.php
的脚本,它会向你的数据库插入表单数据。初始的表单像下面这样:

<input name="username" type="text" size="15" maxlength="64">

运行 process.php 的时候,启用了注册全局变量的 PHP 会将该参数赋值到
$username 变量。这会比通过 $_POST[‘username’]
$_GET[‘username’]
访问它节省击键次数。不幸的是,这也会给你留下安全问题,因为 PHP
会设置该变量的值为通过 GET 或 POST
的参数发送到脚本的任何值,如果你没有显示地初始化该变量并且你不希望任何人去操作它,这就会有一个大问题。

看下面的脚本,假如 $authorized 变量的值为
true,它会给用户显示通过验证的数据。正常情况下,只有当用户正确通过了这个假想的
authenticated_user() 函数验证,$authorized
变量的值才会被设置为真。但是如果你启用了
register_globals,任何人都可以发送一个 GET 参数,例如 authorized=1
去覆盖它:

<?php
// Define $authorized = true only if user is authenticated
if (authenticated_user()) {
    $authorized = true;
}
?>

这个故事的寓意是,你应该从预定义的服务器变量中获取表单数据。所有通过
post 表单传递到你 web 页面的数据都会自动保存到一个称为 $_POST
的大数组中,所有的 GET 数据都保存在 $_GET
大数组中。文件上传信息保存在一个称为 $_FILES
的特殊数据中。另外,还有一个称为 $_REQUEST 的复合变量。

要从一个 POST 方法表单中访问 username 字段,可以使用
$_POST[‘username’]。如果 username 在 URL 中就使用
$_GET[‘username’]。如果你不确定值来自哪里,用
$_REQUEST[‘username’]

<?php
$post_value = $_POST['post_value'];
$get_value = $_GET['get_value'];
$some_variable = $_REQUEST['some_value']; 
?>

$_REQUEST 是 $_GET、$_POST、和 $_COOKIE
数组的结合。如果你有两个或多个值有相同的参数名称,注意 PHP
会使用哪个。默认的顺序是 cookie、POST、然后是 GET。

推荐安全配置选项

这里有几个会影响安全功能的 PHP
配置设置。下面是一些显然应该用于生产服务器的:

  • register_globals 设置为 off
  • safe_mode 设置为 off
  • error_reporting 设置为
    off。如果出现错误了,这会向用户浏览器发送可见的错误报告信息。对于生产服务器,使用错误日志代替。开发服务器如果在防火墙后面就可以启用错误日志。(LCTT
    译注:此处据原文逻辑和常识,应该是“开发服务器如果在防火墙后面就可以启用错误报告,即
    on。”)
  • 停用这些函数:system()、exec()、passthru()、shell_exec()、proc_open()、和
    popen()。
  • open_basedir 为 /tmp(以便保存会话信息)目录和 web
    根目录,以便脚本不能访问这些选定区域外的文件。
  • expose_php 设置为 off。该功能会向 Apache 头添加包含版本号的 PHP
    签名。
  • allow_url_fopen 设置为
    off。如果你能够注意你代码中访问文件的方式-也就是你验证所有输入参数,这并不严格需要。
  • allow_url_include 设置为
    off。对于任何人来说,实在没有明智的理由会想要访问通过 HTTP
    包含的文件。

一般来说,如果你发现想要使用这些功能的代码,你就不应该相信它。尤其要小心会使用类似
system() 函数的代码-它几乎肯定有缺陷。

启用了这些设置后,让我们来看看一些特定的攻击以及能帮助你保护你服务器的方法。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签:, , , , , , , , , , ,
网站地图xml地图