1

基本上,我已经在一个网站上工作了几个月,我即将打开它。在开幕之前,我将讨论潜在的安全问题,并且我在网络上进行了一些研究以找出 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().

这是否足够,我可以改进它,还是我只需要完全改变它?

4

7 回答 7

3

这部分很危险:

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

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

一个简单的修复可能是:

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

不要忘记对实际应用同样的东西require

require basename($_GET['page']) . '.php';

您也可以basename()进一步向上应用,这样您就不必两次应用该功能

也可以看看:basename

于 2013-03-03T08:30:55.020 回答
0

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

我建议验证斜线和点不在字符串中(因为 Windows 中的路径如 ../ 或 / 或 \)。甚至可能在最后硬编码一个 .PHP 以防止其他文件类型被访问。

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

include($_GET['page'] .".php");
于 2013-03-03T08:27:35.723 回答
0

根据我的评论,我会做这样的事情。

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

然而,这不是解决这个问题的最佳方式。

于 2013-03-03T08:29:52.150 回答
0

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

于 2013-03-03T08:32:02.390 回答
0

这将包括所有允许的页面,因此 $_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");
}
于 2013-03-03T08:43:51.600 回答
0

我会使用白名单:

$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;
于 2013-03-03T09:18:45.127 回答
0

很多这些答案都忘记了空字节攻击。附加“.php”以防止诸如 ../../../../../etc/passwd 之类的事情在许多部署中不起作用。此问题仅在PHP 5.3.4中正确修复。

于 2013-03-22T03:27:44.530 回答