PHP $ SERVER ['HTTP HOST'] против $ 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 (комментарий: Vladimir Kornea 14-Mar-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'
(по крайней мере, это то, что я заметил в virtualhosts на основе портов Apache)
как я отметил ниже, 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);
}
использовать. Они оба одинаково (в)безопасны, так как во многих случаях ИМЯ_СЕРВЕРА просто заполняется из HTTP_HOST. Обычно я использую HTTP_HOST, чтобы пользователь оставался на точном имени хоста, с которого он начал. Например, если у меня такой же сайт на a .com and .org domain, я не хочу отправлять кого-то из .орг .com, особенно если у них могут быть маркеры входа в систему .организация, которую они потеряют, если отправят в другой домен.
в любом случае, вам просто нужно убедиться, что ваш webapp всегда будет отвечать только за известные-хорошие домены. Это можно сделать либо (a) с помощью проверки на стороне приложения, такой как Gumbo's, либо (b) с помощью виртуального хоста на доменном имени(именах), которое вы хотите, чтобы не отвечает к запросам, которые дают неизвестный заголовок Хоста.
причина этого в том, что если вы разрешаете доступ к вашему сайту под любым старым именем, вы открываете себя для атак DNS rebinding (где имя хоста другого сайта указывает на ваш IP, пользователь обращается к вашему сайту с помощью атакующего хоста, то имя переместится в злоумышленника ИС с куки/авт с ним) и поисковик угон (когда злоумышленник указывает свои имя хоста на ваш сайт и пытается заставить поисковые системы видят его как "лучший" основное имя хоста).
по-видимому, обсуждение в основном касается $_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 будет реализовывать его с помощью надежный четко определенное поведение. (В отличие от другое.) Тем не менее, это все еще полностью зависит от SAPI ➫ ➫:
нет никакой гарантии, что каждый веб-сервер предоставит любую из них [
$_SERVER
записи]; сервер может опустить некоторые из них или предоставить другие, не перечисленные здесь.
чтобы понять, как правильно получить имя хоста, в первую очередь вам нужно понять, что сервер, который содержит только код не имеет средств знать (предпосылка для проверки) имя собственное на сеть. Он должен взаимодействовать с компонентом, который предоставляет ему собственное имя. Это можно сделать с помощью:
локальный файл config
локальная база данных
жестко закодированный исходный код
внешний запрос (скручиваемость)
клиент/нападающий
Host:
запросetc
обычно это делается через локальный файл конфигурации (SAPI). Обратите внимание, что вы настроили его правильно, например, в Apache ➫ ➫:
пару вещей нужно "подделать", чтобы динамический виртуальный хост выглядел как обычный.
наиболее важным является имя сервера, которое используется Apache для создания самореферентных URL-адресов и т. д. Он настроен с
ServerName
директива, и она доступна для CGIs черезSERVER_NAME
переменные среды.фактическое значение, используемое во время выполнения контролируется параметр UseCanonicalName.
С
UseCanonicalName Off
имя сервера происходит от содержимогоHost:
заголовок в запросе. СUseCanonicalName DNS
это происходит от обратного поиска DNS IP-адреса виртуального хоста. Прежняя настройка используется для name-based динамический виртуальный хостинг, причем последний используется для * * IP-хостинга.если Apache не может разработать имя сервера, потому что нет
Host:
заголовок или поиск DNS не удается затем значение, настроенное с помощьюServerName
используется вместо этого.
Я не уверен, и не очень доверяю $_SERVER['HTTP_HOST']
потому что это зависит от заголовка от клиента. С другой стороны, если домен, запрошенный клиентом, не мой, они не попадут на мой сайт, потому что DNS и протокол TCP/IP указывают на правильное назначение. Однако я не знаю, возможно ли захватить DNS, сеть или даже сервер Apache. Чтобы быть в безопасности, я определяю имя хоста в среде и сравниваю его с $_SERVER['HTTP_HOST']
.
добавить SetEnv MyHost domain.com
in .htaccess файл на корень и добавить ths код в Общий.в PHP
if (getenv('MyHost')!=$_SERVER['HTTP_HOST']) {
header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
exit();
}
Я включаю это общее.php файл на каждой странице php. Эта страница делает все необходимое для каждого запроса, например session_start()
, изменить cookie сеанса и отклонить, если метод post пришел из другого домена.
XSS
всегда будет там, даже если вы используете $_SERVER['HTTP_HOST']
, $_SERVER['SERVER_NAME']
или $_SERVER['PHP_SELF']