PHP $ SERVER ['HTTP HOST'] vs. $ SERVER ['server NAME'], правильно ли я понимаю man-страницы?
Я много искал, а также читал PHP $ _SERVER docs. Имею ли я это право в отношении того, что использовать для моих PHP-скриптов для простых определений ссылок, используемых на моем сайте?
$_SERVER['SERVER_NAME']
основан на конфигурационном файле вашего веб-сервера (Apache2 в моем случае) и зависит от нескольких директив: (1) VirtualHost, (2) ServerName, (3) UseCanonicalName и т. д.
$_SERVER['HTTP_HOST']
основано на запросе от клиента.
поэтому мне кажется, что правильным использовать для того, чтобы сделать мои скрипты максимально совместимыми, было бы $_SERVER['HTTP_HOST']
. Правильно ли это предположение?
продолжение комментария:
Я думаю, что я немного параноик после прочтения этой статьи и отметив, что некоторые люди сказали: "они не будут доверять ни одному из $_SERVER
Варс":
http://markjaquith.wordpress.com/2009/09/21/php-server-vars-not-safe-in-forms-or-links/
http://php.net/manual/en/reserved.variables.server.php#89567 (комментарий: Владимир Корнея 14-мар-2009 01: 06)
по-видимому, речь идет в основном о $_SERVER['PHP_SELF']
и почему вы не должны использовать его в атрибуте действия формы без надлежащего экранирования, чтобы предотвратить XSS атаки.
мой вывод о моем первоначальном вопросе выше заключается в том, что он" безопасен " для использования $_SERVER['HTTP_HOST']
для всех ссылок на сайте без необходимости беспокоиться о XSS атак, даже при использовании в формах.
пожалуйста, поправьте меня, если я ошибаюсь.
8 ответов:
Это, наверное, первая мысль каждого. Но это немного сложнее. Смотрите статья Криса Шифлетта
SERVER_NAME
иHTTP_HOST
.кажется, что нет серебряной пули. Только когда ты заставить Apache использовать каноническое имя вы всегда получите правильное имя сервера
SERVER_NAME
.Итак, вы либо идете с этим, либо проверяете имя хоста против белого списка:
$allowed_hosts = array('foo.example.com', 'bar.example.com'); if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $allowed_hosts)) { header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request'); exit; }
просто дополнительное примечание-если сервер работает на порту, отличном от 80 (как это может быть распространено на машине разработки / интрасети), то
HTTP_HOST
содержит порт, в то время какSERVER_NAME
нет.$_SERVER['HTTP_HOST'] == 'localhost:8080' $_SERVER['SERVER_NAME'] == 'localhost'
(по крайней мере, это то, что я заметил в Apache port-based virtualhosts)
как я отметил ниже,
HTTP_HOST
тут не содержат:443
при работе на HTTPS (если вы не работаете на нестандартном порту, который я не тестировал).
это подробный перевод того, что Symfony использует для получения имени хоста (смотрите второй пример для более буквального перевода):
function getHost() { $possibleHostSources = array('HTTP_X_FORWARDED_HOST', 'HTTP_HOST', 'SERVER_NAME', 'SERVER_ADDR'); $sourceTransformations = array( "HTTP_X_FORWARDED_HOST" => function($value) { $elements = explode(',', $value); return trim(end($elements)); } ); $host = ''; foreach ($possibleHostSources as $source) { if (!empty($host)) break; if (empty($_SERVER[$source])) continue; $host = $_SERVER[$source]; if (array_key_exists($source, $sourceTransformations)) { $host = $sourceTransformations[$source]($host); } } // Remove port number from host $host = preg_replace('/:\d+$/', '', $host); return trim($host); }
устарел:
Это мой перевод на голый PHP метода, используемого в Symfony framework, который пытается получить имя хоста из всех возможных способов в порядке лучшей практики:
function get_host() { if ($host = $_SERVER['HTTP_X_FORWARDED_HOST']) { $elements = explode(',', $host); $host = trim(end($elements)); } else { if (!$host = $_SERVER['HTTP_HOST']) { if (!$host = $_SERVER['SERVER_NAME']) { $host = !empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : ''; } } } // Remove port number from host $host = preg_replace('/:\d+$/', '', $host); return trim($host); }
использовать. Они оба одинаково (в)безопасны, так как во многих случаях SERVER_NAME просто заполняется из HTTP_HOST в любом случае. Я обычно иду на HTTP_HOST, так что пользователь остается на точное имя хоста, на котором они начали. Например, если у меня есть один и тот же сайт в домене .com и .org, я не хочу отправлять кого-то из .org в .com, особенно если у них могут быть маркеры входа в .org, которые они потеряют при отправке в другой домен.
в любом случае, вы просто должны быть уверены, что ваш веб-приложение будет отвечать только за известные хорошие домены. Это можно сделать либо (а) с помощью проверки на стороне приложения, такой как Gumbo's, либо (б) с помощью виртуального хоста на доменных именах, которые вы хотите, чтобы не отвечает к запросам, которые дают неизвестный заголовок узла.
причина этого заключается в том, что если вы разрешаете доступ к вашему сайту под любым старым именем, вы открываете себя для атак повторной привязки DNS (где имя хоста другого сайта указывает на ваш IP, пользователь обращается к вашему сайту с помощью имя хоста злоумышленника, затем имя хоста перемещается на IP-адрес злоумышленника, принимая ваши куки / auth с ним) и захват поисковой системы (где злоумышленник указывает свое собственное имя хоста на вашем сайте и пытается заставить поисковые системы видеть его как "лучшее" основное имя хоста).
по-видимому, речь идет в основном о $_SERVER['PHP_SELF'] и почему вы не должны использовать его в атрибуте действия формы без надлежащего экранирования для предотвращения атак XSS.
Pfft. Ну вы не должны использовать что-нибудь на любой атрибут без экранирования с
htmlspecialchars($string, ENT_QUOTES)
, Так что там нет ничего особенного о переменных сервера.
основное различие между ними состоит в том, что
$_SERVER['SERVER_NAME']
- это управляемая сервером переменная, в то время как$_SERVER['HTTP_HOST']
- это контролируемое пользователем значение.эмпирическое правило-никогда не доверять значениям от пользователя, поэтому
$_SERVER['SERVER_NAME']
- Это лучший выбор.как указал Гумбо, Apache построит SERVER_NAME из предоставленных Пользователем значений, если вы не установите
UseCanonicalName On
.Edit: сказав Все это, если сайт использует виртуальный хост на основе имени, заголовок Хоста HTTP является единственный способ добраться до сайтов, которые не являются сайтом по умолчанию.
это "безопасно" использовать
$_SERVER['HTTP_HOST']
для всех ссылок на сайте без необходимости беспокоиться о XSS атак, даже при использовании в формах?Да, это безопасное использовать
$_SERVER['HTTP_HOST']
, (и даже$_GET
и$_POST
)пока вы их проверяете прежде чем принимать их. Это то, что я делаю для безопасных производственных серверов:/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ $reject_request = true; if(array_key_exists('HTTP_HOST', $_SERVER)){ $host_name = $_SERVER['HTTP_HOST']; // [ need to cater for `host:port` since some "buggy" SAPI(s) have been known to return the port too, see http://goo.gl/bFrbCO $strpos = strpos($host_name, ':'); if($strpos !== false){ $host_name = substr($host_name, $strpos); } // ] // [ for dynamic verification, replace this chunk with db/file/curl queries $reject_request = !array_key_exists($host_name, array( 'a.com' => null, 'a.a.com' => null, 'b.com' => null, 'b.b.com' => null )); // ] } if($reject_request){ // log errors // display errors (optional) exit; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ echo 'Hello World!'; // ...
преимущество
$_SERVER['HTTP_HOST']
это то, что его поведение более четко определено, чем$_SERVER['SERVER_NAME']
. Контрастный ➫ ➫:содержание заголовка Host: из текущего запроса, если он есть.
С:
имя хоста, на котором выполняется текущий скрипт.
используя более определенный интерфейс как
$_SERVER['HTTP_HOST']
означает, что больше SAPIs будет реализовывать его с помощью надежный четко определенное поведение. (В отличие от другое.) Тем не менее, это все еще совершенно САПИ зависимая ➫ ➫:нет никакой гарантии, что каждый веб-сервер предоставит любую из них [
$_SERVER
записи]; сервер может опустить некоторые из них или предоставить другие, не перечисленные здесь.чтобы понять, как правильно получить имя хоста, в первую очередь вам нужно понять, что сервер, который содержит только код не имеет возможности знать (необходимое условие для проверки)имя собственное на сеть. Он должен взаимодействовать с компонентом, который предоставляет ему свое собственное имя. Это можно сделать с помощью:
локальный файл config
локальная база данных
жестко исходный код
внешний запрос (curl)
клиент/нападающий
Host:
запросetc
обычно это делается через локальный (SAPI) конфигурационный файл. Обратите внимание, что вы настроили его правильно, например, в Apache ➫ ➫:
несколько вещей должны быть "подделаны", чтобы динамический виртуальный хост выглядел как обычный.
наиболее важным является имя сервера, которое используется Apache для создания самореферентных URL-адресов и т. д. Он настроен с помощью
ServerName
директива, и она доступна для CGIs черезSERVER_NAME
переменные среды.фактическое значение, используемое во время выполнения контролируется параметр UseCanonicalName.
С
UseCanonicalName Off
имя сервера происходит из содержимогоHost:
заголовок в запросе. СUseCanonicalName DNS
это происходит от обратного DNS-поиска IP-адреса виртуального хоста. Первый параметр используется для имен на основе динамический виртуальный хостинг, причем последний используется для * * IP-хостинга.если Apache не может выработать имя сервера, потому что нет
Host:
заголовок или поиск DNS не удается затем значение, настроенное с помощьюServerName
используется вместо этого.
Я не уверен, и не очень доверяю
$_SERVER['HTTP_HOST']
потому что это зависит от заголовка от клиента. С другой стороны, если домен, запрошенный клиентом, не является моим, они не попадут на мой сайт, потому что протокол DNS и TCP/IP указывают на правильное назначение. Однако я не знаю, можно ли захватить DNS, сеть или даже сервер Apache. Чтобы быть в безопасности, я определяю имя хоста в среде и сравниваю его с$_SERVER['HTTP_HOST']
.добавить
SetEnv MyHost domain.com
in .файл htaccess в корневом каталоге и добавьте код ths Общий.phpif (getenv('MyHost')!=$_SERVER['HTTP_HOST']) { header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request'); exit(); }
Я включаю это общее.php-файл на каждой странице php. Эта страница делает все необходимое для каждого запроса, как
session_start()
, изменить cookie сеанса и отклонить, если метод post пришел из другого домена.