知识屋:更实用的电脑技术知识网站
所在位置:首页 > 编程技术 > PHP编程

PHP最主要的7个安全漏洞

发布时间:2011-02-18 18:36:35作者:知识屋

对于快速发展的动态网页而言,PHP是一种了不起的语言。PHP也具有对初级程序员友好的特点,比如PHP就不需要动态声明。然而,这些特征可能导致一个程序员无意地让安全漏洞潜入到web应用程序中。在PHP应用中,流行的安全邮件列表就出现大量被证实的漏洞,但是一旦你明白PHP应用程序中常见的几种漏洞的基本类型,那你将发现它和其他语言是同样安全的。

  在这篇文章中,我将详细地介绍会导致安全漏洞的几种常用见的PHP程序缺陷。通过向你们展示什么是不能做的,并且如何利用每个特定的缺陷,我希望你们不仅仅能明白怎样避免这些特定的缺陷,而且为什么这些错误能导致安全漏洞。

  明白每个可能出现的缺陷,将帮助你们避免在PHP应用程序中产生同样的错误。

  安全是一个过程,不是一个产品在应用程序开发过程中采用对安全有益的方法可以让你生成更紧密,更健壮的代码。

  未校验输入缺陷

  如果不是最常见的PHP安全漏洞,也是其中之一的,就是未校验输入错误。提供数据的用户是根本不能信任的。你应该假定你的web应用程序的用户个个都是心怀叵测的,因为他们中的一些就是那样的。未校验或不正确验证输入是被一些漏洞所利用的根源,我们将在本文后面进行讨论。

  例如,你可能写一个允许用户查看日历的如下代码,通过调用UNIX的cal命令来显示指定月份。

  $month = $_GET['month'];

  $year = $_GET['year'];

  exec("cal $month $year", $result);

  print "

";

 

  foreach ($result as $r) { print "$r
"; }

  print "

";

 

  此代码具有一个安全漏洞缝隙,因为没有以任何的方式来验证$_GET[month]和$_GET[year]变量。只要那个特定的月份是在1到12之间,并且提供一个合适的四位数年份,那这个应用程序将完美运行。然而,恶意用户可能追加“; ls - la”到年参数,从而看到您网站的HTML目录列表。一个极端恶劣的用户可能追加";rm -rf *"到年参数,且删除整个网站!

  纠正这种错误的合适的方法就是确保你从用户接受的输入是你期望得到的。不用为这种错误使用JavaScript验证,创造他们自己形式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 "

";

 

  foreach ($result as $r) { print "$r
"; }

  print "

";

 

  不用担心用户提供影响你应用程序的输入或是运行输入的服务器,你能安全地使用代码。正则表达式是一个很棒的验证输入的工具。尽管难以掌握它,但在这种情况下是非常有用的。

  你应该总是通过拒绝与你期望数据不相符合的数据,来验证你的用户提供的数据。永远都不要使用在你知道期望数据是有害的情况下仍然接受此数据的方法,此方法是安全漏洞的共同来源。有时,恶意的用户能避开此种方法,例如,用空字符来掩盖坏输入的方法。如此的输入将通过检查,但是它仍然具有坏的影响。

  当你验证任何输入时,你应当尽可能的严格。如果有一些没必要包含的字符,可能的话,你应该要么去除那些无用的字符,要么完全拒绝输入。

  访问控制缺陷

  另一个缺陷,不一定限于PHP应用程序,但仍然是重要的,是访问控制的脆弱性类型。当你的应用程序的某些部分的应用是限定于某些用户的时候,这种缺陷就出现了,如,一个允许更改配置设置或显示敏感信息的管理页面。

  你应该检查每个你的PHP应用程序页面限制加载的用户的访问权利。如果你仅仅只检查在索引页面的用户证书,那么一个恶意的用户能直接进入一个“更深层”网页的链接,这将跳过证书检查的过程。

  如,如果你的网站有攻击用户的可预测IP或固定IP地址,则可以通过限制用户访问该用户的基本IP地址和他们用户的名字在你程序的安全层上是有利的。放置你的受限制的网页在一个由apache.htaccess文件保护的独立的目录里也是一个好的做法。

  将配置文件放置在你web访问目录的外面。一个配置文件包含数据库密码和其他一些能被恶意用户用来渗透或者破坏你站点的信息;从来不让远程用户访问这些文件。使用PHP的include函数来包含这些来自不可web访问的目录的文件,万一这个目录曾因管理员误操作而产生web访问,这可能包括含有“否定一切”的an.htaccess文件。尽管分层安全是多余的,但是它是一件积极的事情。

对于快速发展的动态网页而言,PHP是一种了不起的语言。PHP也具有对初级程序员友好的特点,比如PHP就不需要动态声明。然而,这些特征可能导致一个程序员无意地让安全漏洞潜入到web应用程序中。在PHP应用中,流行的安全邮件列表就出现大量被证实的漏洞,但是一旦你明白PHP应用程序中常见的几种漏洞的基本类型,那你将发现它和其他语言是同样安全的。

  在这篇文章中,我将详细地介绍会导致安全漏洞的几种常用见的PHP程序缺陷。通过向你们展示什么是不能做的,并且如何利用每个特定的缺陷,我希望你们不仅仅能明白怎样避免这些特定的缺陷,而且为什么这些错误能导致安全漏洞。

  明白每个可能出现的缺陷,将帮助你们避免在PHP应用程序中产生同样的错误。

  安全是一个过程,不是一个产品在应用程序开发过程中采用对安全有益的方法可以让你生成更紧密,更健壮的代码。

  未校验输入缺陷

  如果不是最常见的PHP安全漏洞,也是其中之一的,就是未校验输入错误。提供数据的用户是根本不能信任的。你应该假定你的web应用程序的用户个个都是心怀叵测的,因为他们中的一些就是那样的。未校验或不正确验证输入是被一些漏洞所利用的根源,我们将在本文后面进行讨论。[!--empirenews.page--]分页标题[/!--empirenews.page--]

  例如,你可能写一个允许用户查看日历的如下代码,通过调用UNIX的cal命令来显示指定月份。

  $month = $_GET['month'];

  $year = $_GET['year'];

  exec("cal $month $year", $result);

  print "

";

 

  foreach ($result as $r) { print "$r
"; }

  print "

";

 

  此代码具有一个安全漏洞缝隙,因为没有以任何的方式来验证$_GET[month]和$_GET[year]变量。只要那个特定的月份是在1到12之间,并且提供一个合适的四位数年份,那这个应用程序将完美运行。然而,恶意用户可能追加“; ls - la”到年参数,从而看到您网站的HTML目录列表。一个极端恶劣的用户可能追加";rm -rf *"到年参数,且删除整个网站!

  纠正这种错误的合适的方法就是确保你从用户接受的输入是你期望得到的。不用为这种错误使用JavaScript验证,创造他们自己形式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 "

";

 

  foreach ($result as $r) { print "$r
"; }

  print "

";

 

  不用担心用户提供影响你应用程序的输入或是运行输入的服务器,你能安全地使用代码。正则表达式是一个很棒的验证输入的工具。尽管难以掌握它,但在这种情况下是非常有用的。

  你应该总是通过拒绝与你期望数据不相符合的数据,来验证你的用户提供的数据。永远都不要使用在你知道期望数据是有害的情况下仍然接受此数据的方法,此方法是安全漏洞的共同来源。有时,恶意的用户能避开此种方法,例如,用空字符来掩盖坏输入的方法。如此的输入将通过检查,但是它仍然具有坏的影响。

  当你验证任何输入时,你应当尽可能的严格。如果有一些没必要包含的字符,可能的话,你应该要么去除那些无用的字符,要么完全拒绝输入。

  访问控制缺陷

  另一个缺陷,不一定限于PHP应用程序,但仍然是重要的,是访问控制的脆弱性类型。当你的应用程序的某些部分的应用是限定于某些用户的时候,这种缺陷就出现了,如,一个允许更改配置设置或显示敏感信息的管理页面。

  你应该检查每个你的PHP应用程序页面限制加载的用户的访问权利。如果你仅仅只检查在索引页面的用户证书,那么一个恶意的用户能直接进入一个“更深层”网页的链接,这将跳过证书检查的过程。

  如,如果你的网站有攻击用户的可预测IP或固定IP地址,则可以通过限制用户访问该用户的基本IP地址和他们用户的名字在你程序的安全层上是有利的。放置你的受限制的网页在一个由apache.htaccess文件保护的独立的目录里也是一个好的做法。

  将配置文件放置在你web访问目录的外面。一个配置文件包含数据库密码和其他一些能被恶意用户用来渗透或者破坏你站点的信息;从来不让远程用户访问这些文件。使用PHP的include函数来包含这些来自不可web访问的目录的文件,万一这个目录曾因管理员误操作而产生web访问,这可能包括含有“否定一切”的an.htaccess文件。尽管分层安全是多余的,但是它是一件积极的事情。


对于我的PHP应用程序,我更喜欢基于一下样本的目录结构。所有的功能库,类和配置文件存储在includes目录里。这些include文件总是以a.php扩展名命名,因此即便是跳过所有你的保护,web服务器将解析PHP代码,且不会将它显示给用户。www和管理目录是唯一的目录,它们的文件由一个URL直接被访问,管理目录由an.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

  你应该设置你的Apache目录索引到“index.php”,并且在每个目录中保持一个index.php文件。如果不能浏览目录,如一个图片目录或是相似的目录,则设置Apache目录来重定向你的主页。永远都不要通过增加.bak或其他扩展到文件名复制一个php文件在你的公开的web目录里。根据你使用的web服务器(Apache幸而似乎对此服务器有一定保障),此服务器不会解析文件中的PHP代码,可能作为资源输出给碰巧得到拷贝文件URL的用户。如果那个文件包含密码或是其他隐秘信息,此文件将是可读的,如果碰巧给黑客发现,那此文件可能甚至不被Google索引。将文件重命名为a.bak.php的扩展比套接a.bak到.php扩展要更安全,但是最好的解决方法是使用一个源代码版本控制系统比如CVS。尽管CVS学起来很难,但是你花费的时间将在许多方面得到补偿。该系统节省你项目中的每个文件的每个版本,当此后要改变导致的问题时,它能具有无法估量的价值。

  会话ID保护

  拦截会话ID是PHP网站的一个问题。PHP会话跟踪组建为每一个用户会话使用唯一的ID,但是如果其他用户知道了这个ID,此用户能拦截这个ID的用户会话并能看到秘密信息。拦截会话ID是完全不能被阻止的,你应该知道一些风险以便减轻他们。

  例如,甚至在给用户验证和分配了一个会话ID后,当他或是她进行任何高度敏感行为时,如重设密码哦,你都应该重新验证该用户。如,绝不允许一个会话验证的用户没有进入旧的密码就能进入一个新的密码。你也应该避免呈现真正的秘密数据给只被会话ID验证的用户,如信用卡号。[!--empirenews.page--]分页标题[/!--empirenews.page--]

  应该使用session_regenerate_id函数为通过登陆创建新会话的用户安排一个新的会话ID。一个拦截用户将试图用他之前的会话ID登陆,如果在登陆时设置你的ID,就会阻止此事情的发生。

  如果你的站点正处理很重要的信息,如信用卡,必须一直使用一个SSL安全连接。由于不能发现会话ID且不容易拦截它,这将减少会话拦截的漏洞。如果你的网站运行在共享的Web服务器上,要知道,在同一服务器上的任何其他用户都很容易地查看任何会话变量。通过在数据库记录中存储所有的敏感数据来减轻这个漏洞,此数据库记录关键在于会话ID而不是作为会话变量。如果你必须在会话变量中存储密码(我再次强调,避免出现这种情况是最好的),不要在明文中存储密码;使用sha1() (PHP 4.3+) or md5()函数来存储密码的哈希值来代替。

  if ($_SESSION['password'] == $userpass) {

  // do sensitive things here

  }

  以上代码是不安全的,因为密码存储在会话变量的明文中。相反,更可能像这样使用代码:

  if ($_SESSION['sha1password'] == sha1($userpass)) {

  // do sensitive things here

  }

  SHA-1算法并不是没有缺陷,计算机能力的发展使得它有可能产生碰撞(同一SHA-1总数具有不同字符串)。然而此技术存储密码仍然大大优于在明文中存储密码。如果你必须使用MD5,因为这优于明文中存储密码,但是请记住,近来的发展已经使它在标准的PC硬件上可能不到一小时就产生MD5碰撞。理想情况下,应该使用一个函数来实现SHA-256,如此的函数在当前是与PHP相独立的。

  为了进一步了解哈希碰撞,在其他一些与安全相关的主题中,Bruce Schneier's Website是最好的资源。

  跨站点脚本(XSS)缺陷

  跨站点脚本或XSS缺陷是用户验证的子集,一个恶意的用户在被呈现的且被其他用户执行的数据里嵌入脚本命令—通常是JavaScript。

  例如,如果你的应用程序包含一个论坛,在此论坛里,人们发出的消息能被其他用户看到,恶意用户可以嵌入一个

  为了阻止这类攻击的出现,你必须在网页中小心地显示用户提交的字面内容。防范这最容易的方法就是只要避免组成HTML语法的字符(特别是<和>)到HTML字符实体(<和>),以便为了显示将提交的数据作为明文看待。通过PHP的htmlspecialchars函数传递数据仅仅就像你正在输出一样。

  如果你的应用程序要求你的用户可以提交HTML内容,并将它当成这样,你反而会需要过滤出像


 

(免责声明:文章内容如涉及作品内容、版权和其它问题,请及时与我们联系,我们将在第一时间删除内容,文章内容仅供参考)
收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜