避免使用include($_GET['page'])


Avoiding using include($_GET['page'])

基本上,我已经在一个网站上工作了几个月了,我马上就要打开它了。在开放之前,我正在讨论潜在的安全问题,我已经在网上做了一些研究,以找出php中常见的安全问题,我已经知道如何解决其中的大多数问题,尽管我真的有一个问题:我想避免使用include($_GET['page']),我已经做了一些研究,但没有发现任何真正方便使用的东西,尽管有任何方法我可以"保护"我的代码,因为它是?
以下是我为防止安全问题编写的代码:

if (!isset($_GET['page'])) 
{
echo redirect_tempo(500, 'index.php?page=home');    
}
    elseif ($_GET['page']=="index") 
    {
    echo redirect_tempo(500, 'index.php?page=home');        
    }
        elseif (file_exists($_GET['page'].".php"))
        {
        require $_GET['page'].'.php';
        }
            else 
            {
            echo redirect_tempo(500, 'index.php?page=404');
            }

注意redirect_temp()基本上只是一个header()

这样就够了吗?我可以改进它吗?还是我只需要完全改变它?

这部分很危险:

elseif (file_exists($_GET['page'].".php"))

我可以给路径"../../../../etc/passwd"(我知道,这有点旧,但它可以是任何东西),它会读取文件(给予足够的权限)。

一个简单的修改可以是:

elseif (file_exists(basename($_GET['page']) . ".php"))

不要忘记对实际的require:

应用同样的东西
require basename($_GET['page']) . '.php';

你也可以在上面应用basename(),这样你就不必应用两次函数

参见:basename

按照我的评论,我会这样做。

switch($_GET['page']) {
    case 'index' :
        redirect_tempo(500, 'index.php?page=home');
    break;
    ........
    default :
        die('NO!');
    break;
}

您的代码看起来不错。但是,如果在使用之前也验证GET值,效果会更好。您可以检查它是仅为数字还是仅为字母或字母数字。可以是单个单词,也可以是多个单词。

使用你的方法没有错。只需添加一些基本的验证,这样您就知道恶意用户不会尝试访问其他路径。

我建议验证斜杠和点不在字符串中(因为路径像..)/或/或'在Windows中)。甚至可以在末尾硬编码一个。php,以防止其他文件类型被访问。

if (strstr($_GET['page'], ".") || strstr($_GET['page'], "/") || strstr($_GET['page'], "''")
{
    echo "error";
    exit;
}
include($_GET['page'] .".php");

这将包括所有允许的页面,所以$_GET['page'] == search将包括search.php,但$_GET['page'] == something将包括home.php:]

$allowed_pages = array('home', 'search', 'signup', 'signin', 'loggedin');
if(in_array($_GET["page"], $allowed_pages)) {
  $page = $_GET["page"]; 
} else { 
  $page = "home"; 
}
  $page = str_replace('/', '', $page);
if(file_exists("page_includes/$page.php")) {
  include("page_includes/$page.php");
} else {
  include("page_includes/home.php");
}

我会使用白名单:

$all_pages = array(
    'index' => 'index.php',
    'about' => 'misc/about.php',
    '404' => '404.php',
    ...
);
function reverse($filename) {
    /* maps filenames to page names */
    return preg_replace('#/pages/(.*).php#', '$1', $filename);
}
/* add all php files in pages/ directory to $all_pages */
foreach (glob("pages/*.php") as $filename) {
    $all_pages[reverse($filename)] = $filename;
}
/* for debugging: make sure this only prints stuffs you want to include */
/* var_dump($all_pages); */
/* the actual check is simple */
$page_name = isset($_GET['page']) ? $_GET['page'] : "index";
$include_name = isset($all_pages[$page_name]) ? $all_pages[$page_name] : "404";
require $include_name;

很多答案都忘记了空字节攻击。附加"。php"以防止之类的东西 ../../../../../etc/passwd在许多部署中不能工作。