В основном, я работаю над сайтом уже несколько месяцев, и я собираюсь его открыть. Перед открытием я перехожу к потенциальным проблемам безопасности, и я провел некоторое исследование в Интернете, чтобы узнать, что такое проблемы безопасности cummon в 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.php, но $ _GET ['page'] == что-то будет включать 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"); }
в$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"); }
в$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, не будет работать во многих развертываниях. Эта проблема была исправлена только в PHP 5.3.4 .