2009-10-13 3 views
1

Я работаю на настройки, где URL-адрес будет по линиям:Безопасный способ включения страницы из параметра 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. Есть ли что-нибудь еще, о чем я должен помнить?

+0

Я думаю, что установка $ _GET ['page'] в '../../another/directory/file' может вызвать проблемы.Я оставлю его кому-то еще, чтобы проверить и показать вам способы обойти это. – MitMaro

+0

AFAIK, который не вызовет никаких проблем. '$ page' получил бы что-то вроде'/var/www/pages /../ directory/file', который ** не ** разрешает '/ var/www/directory/file', правильно? – DisgruntledGoat

+1

На самом деле я просто попробовал это, и '../' в середине пути ** действительно делает это, поднимая каталог. Я никогда не ожидал, что ... – DisgruntledGoat

ответ

4

код нормально, за исключением того, что вы должны проверить параметр перед использованием:

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

я не буду рекомендовать «переключатель» подхода, поскольку он уменьшает гибкость, которая является всем смысл использования динамики включает в себя ,

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';); 
    } 
} 

Таким образом, это все управляется внутри одного класса и будущих изменений легче сделать без рытья через другой код

отредактированной выше, чтобы отразить запрос.

+2

Для этого я думаю, что гораздо проще иметь плоский массив '{'home', 'about'}', а затем, если запрос соответствует одному из них, просто включите его (завернутый в путь и т. Д.). Имена файлов, которые будут включены, всегда будут соответствовать странице, присутствующей в URL-адресе. – DisgruntledGoat

+0

уверен, что это сработает. Я отредактирую, чтобы отразить это в –

+0

. Ключевым моментом во втором является то, что подпись лобкового метода не изменилась вообще. Если по дороге вы хотите добавить больше сложностей или добавить уровень безопасности и т. Д., Это проще сделать. –

1

Вы также можете переключиться на фреймворк как CodeIgniter, который сделает все это за вас и заставит вас принять некоторые стандарты кодирования, что всегда хорошо.

0

При обращении с произвольным количеством страниц может быть лучше убедиться, что у вас есть дружественные имена файлов. Я бы рекомендовал буквенно-цифровые имена файлов с дефисом или подчеркивает:

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; 
} 
+0

Разве это не плохо для SEO, поскольку вы конвертируете грязный URL-адрес в более аккуратный? Другими словами, что-то вроде 'my; page; name' будет разрешено для' my-page-name', что означает повторяющиеся URL-адреса. Если URL-адрес испорчен, мы не показываем страницу. – DisgruntledGoat

+0

В этом случае он используется в качестве меры предосторожности, поскольку он содержит белые списки, которые запрещают такие символы, как периоды изменения каталогов. Это предполагает, однако, что вы добавляете стандартное расширение файла в конец входящего вызова (т. Е. «.Php») –

0

очень безопасный способ сделать это было бы первым составить список содержимого каталога, затем сопоставить пользовательский ввод в этот список и использовать значение из списка для включения. Что-то в линиях:

$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. В первом случае вы включаете только список, созданный из содержимого каталога, поэтому он будет безопасным, даже если пользователю удастся получить какой-то странный ввод через ваши проверки.

 Смежные вопросы

  • Нет связанных вопросов^_^