基本上,我已经在一个网站上工作了几个月了,我马上就要打开它了。在开放之前,我正在讨论潜在的安全问题,我已经在网上做了一些研究,以找出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在许多部署中不能工作。