从GET参数中包含页面的安全方式?

时间:2009-10-13 23:58:37

标签: php security include get

我正在进行设置,其中的网址将是:

http://example.com/index.php?page=about

实际上,他们会从更简单的URL重写。 index.php将使用以下代码包含其他页面:

if ( isset( $_GET['page'] ) )
{
    $page = $_SERVER['DOCUMENT_ROOT'] . '/pages/' . $_GET['page'] . '.php';
    if ( is_file( $page ) )
        include $page;
    else
        echo 'That page doesn\'t exist.';
}

假设页面文件夹中的所有内容都非常安全,那么此代码是否安全?我已经防范了众所周知的目录黑客,即使用page=../../.passwd。还有什么我应该留意的吗?

5 个答案:

答案 0 :(得分:9)

最好转换它

$page_name = $_GET['page'];

switch($page_name) {
case 'about':
 $page = $_SERVER['DOCUMENT_ROOT'] . '/pages/about.php';
 break;        
case 'home': //fall through to default
case default:
 $page = $_SERVER['DOCUMENT_ROOT'] . '/pages/home.php';
}

include $page;

这样就没有任何注射问题。

修改

另一个解决方案是设置一个专门处理页面名称转换为地址的类。

class Page {
  static private $pages = array ("about", "home");

  const DEFAULT_PAGE = "home";

  static public function includePage($page_name) {
    if (!in_array($page_name, self::$pages)) {
      $page_name = self::DEFAULT_PAGE;
    }
    include ($_SERVER['DOCUMENT_ROOT'] . '/pages/'.$page_name.'.php';);
  }
}

通过这种方式,这一切都在一个类中进行管理,未来的更改更容易进行,而无需深入了解其他代码

以上编辑以反映请求。

答案 1 :(得分:4)

您的代码没问题,除非您在使用前验证参数:

if(!preg_match("~^\w+$~", $_GET['page']))
   die("page id must be alphanumeric!");

我不推荐“切换”方法,因为它降低了灵活性,这是使用动态包含的全部要点。

答案 2 :(得分:1)

您也可以切换到像CodeIgniter这样的框架,它将为您完成所有这些并迫使您采用一些始终是好事的编码标准。

答案 3 :(得分:1)

一种非常安全的方法是首先构建目录内容列表,然后将用户输入与该列表匹配,使用列表中的值包含< / strong>即可。有些东西:

$sdir = $_SERVER['DOCUMENT_ROOT'].'/pages/';
$targetfile = $_GET['page'].'.php';
$filenames = scandir($sdir); // returns an array of directory contents
foreach ($files as $filename) {
  if (($filename[0] != '.')
     && ($filename == $targetfile)
     && (is_file($sdir.$filename)) {
        include $sdir.$filename;
        break;
  }
}

或者您可以通过以下方式完成:

$targetfile = $_GET['page'].'.php';
$sdir = $_SERVER['DOCUMENT_ROOT'].'/pages/';
$filenames = scandir($sdir);
if (in_array($targetfile,$filenames)) {
   include $sdir.$filename;
}

但在后一种情况下,您必须非常确定才能获得正确的检查条件,并使用regex check suggested in another answer。在第一种情况下,您只包含从目录内容构建的列表,因此即使用户设法通过您的支票获得一些奇怪的输入,它也是安全的。

答案 4 :(得分:0)

处理任意数量的页面时,最好确保您拥有SEO友好的文件名。我会推荐带有连字符或下划线的字母数字文件名:

define(DOCROOT, $_SERVER['DOCUMENT_ROOT']);

// assume you do not include file extensions in $_GET['page']
$page = trim(preg_replace('~[^\\pL\d]+~u', '-', $_GET['page']), '-');
if (is_file($page)) {
   include DOCROOT . $page;
}