В чем разница между HTTP_HOST и SERVER_NAME в PHP?

457

Когда вы рассмотрите возможность использования одного над другим и почему?

  • 14
    «Обычно я использую HTTP_HOST, чтобы пользователь оставался на том же имени хоста, с которого он начал. Например, если у меня один и тот же сайт в домене .com и .org, я не хочу отправлять кого-то из .org в .com, особенно если у них могут быть токены входа в систему .org, которые они потеряют, если отправят на другой домен. " - Это и некоторые другие интересные моменты от stackoverflow.com/questions/1459739/…
  • 5
    @ Yarin, не забудьте белый список - проверьте результаты HTTP_HOST . В противном случае злоумышленник может ввести любое значение в HTTP-запросе Host: и заставить сервер принять его.
Показать ещё 1 комментарий
Теги:

9 ответов

665
Лучший ответ

HTTP_HOST получен из заголовка HTTP-запроса, и именно это клиент фактически использовал как "целевой хост" запроса. SERVER_NAME определяется в конфигурации сервера. Какой из них зависит от того, для чего вам это нужно. Теперь вы должны понимать, что это контролируемое клиентом значение, которое, таким образом, не может быть надежным для использования в бизнес-логике, а другое является контролируемым сервером значением, которое является более надежным. Тем не менее вам необходимо убедиться, что веб-сервер имеет правильную конфигурацию SERVER_NAME. Взяв Apache HTTPD в качестве примера, здесь выдержка из ее документации:

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


Обновить: после проверки ответа Pekka на ваш вопрос, который содержит ссылку на bobince answer, что PHP всегда будет возвращать значение HTTP_HOST для SERVER_NAME, что противоречит моему собственному опыту PHP 4.x + Apache HTTPD 1.2.x с пару лет назад, я взорвал пыль от моего текущего XAMPP в Windows XP (Apache HTTPD 2.2.1 с PHP 5.2.8), запустил его, создал страницу PHP, которая печатает оба значения, создала тестовое приложение Java, используя URLConnection, чтобы изменить заголовок Host, и тесты научили меня, что это действительно (неверно) случай.

После первого подозрения PHP и копания в некоторых отчетах об ошибках PHP в отношении предмета, я узнал, что корень проблемы находится на используемом веб-сервере, что он неправильно возвратил заголовок HTTP Host, когда была запрошена SERVER_NAME. Таким образом, я перекопал в отчеты об ошибках Apache HTTPD, используя различные ключевые слова относительно субъект, и я наконец нашел связанную ошибку. Это поведение было введено, так как вокруг Apache HTTPD 1.3. Вам нужно установить UseCanonicalName директиву on в записи <VirtualHost> ServerName в httpd.conf (также проверьте предупреждение внизу документ!).

<VirtualHost *>
    ServerName example.com
    UseCanonicalName on
</VirtualHost> 

Это сработало для меня.

Обобщенный, SERVER_NAME более надежный, но вы зависимый в конфигурации сервера!

  • 3
    Хорошо, это решает мою проблему, которая не связана с ОП, но имеет отношение к делу. Я был очень обеспокоен вопросами безопасности, используя все, что мог предоставить браузер. Этот ответ был ОГРОМНОЙ помощью. Спасибо, что нашли время собрать это вместе.
  • 2
    Почему вы говорите, что HTTP_HOST не надежен? Да, он предоставляется пользователем, но если пользователь предоставит какое-то поддельное значение, конфигурация вашего сервера автоматически вернет 503, и ваш PHP-скрипт даже не запустится!
Показать ещё 6 комментариев
60

HTTP_HOST - целевой хост, отправленный клиентом. Пользователь может свободно манипулировать пользователем. Не нужно посылать запрос на ваш сайт с запросом HTTP_HOST значения www.stackoverflow.com.

SERVER_NAME исходит из определения сервера VirtualHost и поэтому считается более надежным. Его также можно манипулировать извне при определенных условиях, связанных с настройкой вашего веб-сервера. См. Этот Этот вопрос SO, который касается аспектов безопасности обоих вариантов.

Вы не должны полагаться на то, чтобы быть в безопасности. Тем не менее, что использовать, действительно зависит от того, что вы хотите сделать. Если вы хотите определить, в каком домене работает ваш script, вы можете безопасно использовать HTTP_HOST, пока недопустимые значения, поступающие от злоумышленника, не могут ничего сломать.

  • 7
    Да, но запрос, запрашивающий значение HTTP_HOST для www.stackoverflow.com, будет отклонен большинством HTTP-серверов заранее, поэтому скрипт PHP даже не увидит запрос!
  • 2
    @Pacerier true, но не всегда, если сервер настроен неправильно.
Показать ещё 3 комментария
41

Как я упоминал в этом ответе, если сервер работает на порту, отличном от 80 (как это может быть распространено на машине разработки/интрасети), тогда HTTP_HOST содержит порт, а SERVER_NAME - нет.

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(По крайней мере, то, что я заметил в виртуальных хостах на базе Apache)

Обратите внимание, что HTTP_HOST не содержит :443 при работе на HTTPS (если вы не работаете на нестандартном порту, который я не тестировал).

Как отмечали другие, эти два варианта также отличаются при использовании IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
  • 1
    Когда они исправят это коварное поведение?
23

Обратите внимание, что если вы хотите использовать IPv6, вы, вероятно, захотите использовать HTTP_HOST, а не SERVER_NAME. Если вы введете http://[::1]/, переменные среды будут следующими:

HTTP_HOST = [::1]
SERVER_NAME = ::1

Это означает, что если вы делаете mod_rewrite, например, вы можете получить неприятный результат. Пример перенаправления SSL:

# SERVER_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/

# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/

Это относится ТОЛЬКО, если вы обращаетесь к серверу без имени хоста.

  • 0
    SiteGround в своем внутреннем коде перенаправления с http на https используйте https://%{SERVER_NAME}%{REQUEST_URI}
4

если вы хотите проверить через server.php или что вы хотите вызвать со следующим:

<?php

phpinfo(INFO_VARIABLES);

?>

или

<?php

header("Content-type: text/plain");

print_r($_SERVER);

?>

Затем получите доступ ко всем действительным URL-адресам для вашего сайта и проверьте разницу.

3

Мне потребовалось некоторое время, чтобы понять, что люди подразумевают под "SERVER_NAME более надежным". Я использую общий сервер и не имею доступа к директивам виртуального хоста. Итак, я использую mod_rewrite в .htaccess для сопоставления различных HTTP_HOST в разных каталогах. В этом случае это значение HTTP_HOST имеет смысл.

Ситуация аналогична, если вы используете виртуальные хосты на основе имен: директива ServerName внутри виртуального хоста просто говорит, какое имя хоста будет сопоставлено этому виртуальному хосту. Суть в том, что в обоих случаях имя хоста, предоставленное клиентом во время запроса (HTTP_HOST), должно совпадать с именем на сервере, которое само отображается в каталог. Независимо от того, выполняется ли сопоставление с директивами виртуального хоста или с правилами htaccess mod_rewrite, здесь вторично. В этих случаях HTTP_HOST будет таким же, как SERVER_NAME. Я рад, что Apache настроен таким образом.

Однако ситуация отличается от виртуальных хостов на базе IP. В этом случае и только в этом случае SERVER_NAME и HTTP_HOST могут быть разными, потому что теперь клиент выбирает сервер по IP, а не по имени. Действительно, могут быть специальные конфигурации, где это важно.

Итак, начиная с этого момента, я буду использовать SERVER_NAME, на случай, если мой код будет перенесен в эти специальные конфигурации.

3

Зависит от того, что я хочу узнать. SERVER_NAME - это имя хоста сервера, в то время как HTTP_HOST - это виртуальный хост, к которому подключен клиент.

  • 2
    Не совсем верно, Роуленд, SERVER_NAME - это обычно имя VirtualHost, а не сам сервер. А в Apache SERVER_NAME часто заполняется тем же значением, что и HTTP_HOST (см. Ответ BalusC).
  • 0
    @Simon, Так как хосты mosts теперь являются VirtualHost, что бы вы имели в виду под названием «сам сервер»?
Показать ещё 1 комментарий
1

Предполагая, что у вас есть простая настройка (CentOS 7, Apache 2.4.x и PHP 5.6.20) и только один веб-сайт (не предполагающий виртуальный хостинг)...

В смысле PHP $_SERVER['SERVER_NAME'] - это элемент PHP, зарегистрированный в суперкласме $_SERVER на основе вашей конфигурации Apache (директива **ServerName** с UseCanonicalName On) в httpd.conf(будь то из включенной конфигурации виртуального хоста файл, что угодно и т.д.). HTTP_HOST выводится из заголовка HTTP host. Рассматривайте это как пользовательский ввод. Фильтр и проверка перед использованием.

Вот пример того, где я использую $_SERVER['SERVER_NAME'] в качестве основы для сравнения. Следующий метод относится к конкретному дочернему классу, который я назвал ServerValidator (дочерний элемент Validator). ServerValidator проверяет шесть или семь элементов в $_SERVER перед их использованием.

При определении того, является ли HTTP-запрос POST, я использую этот метод.

public function isPOST()
{
    return (($this->requestMethod === 'POST')    &&  // Ignore
            $this->hasTokenTimeLeft()            &&  // Ignore
            $this->hasSameGETandPOSTIdentities() &&  // Ingore
            ($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}

К моменту вызова этого метода будет выполняться вся фильтрация и проверка соответствующих элементов $_SERVER (и соответствующих наборов свойств).

Линия...

($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')

... проверяет, что значение $_SERVER['HTTP_HOST'] (в конечном счете полученное из запрошенного HTTP-заголовка host) соответствует $_SERVER['SERVER_NAME'].

Теперь я использую суперглобальный разговор, чтобы объяснить мой пример, но это потому, что некоторые люди не знакомы с INPUT_GET, INPUT_POST и INPUT_SERVER в отношении filter_input_array().

Суть в том, что я не обрабатываю запросы POST на моем сервере, если не выполнены все четыре условия. Следовательно, с точки зрения запросов POST отказ в предоставлении HTTP host заголовка (присутствия, проверенного для более ранних) заклинаний doom для строгих браузеров HTTP 1.0. Кроме того, запрашиваемый хост должен соответствовать значению ServerName в httpd.conf, а по расширению - значению $_SERVER('SERVER_NAME') в супермаклоне $_SERVER. Опять же, я бы использовал INPUT_SERVER с функциями фильтра PHP, но вы ломали мой дрейф.

Имейте в виду, что Apache часто использует ServerName в стандартных перенаправлениях (например, оставляя конечную косую черту с URL-адресом: Пример http://www.foo.com стать http://www.foo.com/), даже если вы не используете переписывание URL.

Я использую $_SERVER['SERVER_NAME'] как стандарт, а не $_SERVER['HTTP_HOST']. В этом вопросе много вопросов. $_SERVER['HTTP_HOST'] может быть пустым, поэтому это не должно быть основанием для создания кодовых соглашений, таких как мой общедоступный метод выше. Но только потому, что оба могут быть установлены, они не гарантируют, что они будут равны. Тестирование - лучший способ узнать наверняка (имея в виду версию Apache и версию PHP).

0

Как указано в balusC, SERVER_NAME не является надежным и может быть изменен в конфигурации apache, конфигурации сервера сервера и брандмауэра, которые могут находиться между вами и сервером.

Следующая функция всегда возвращает реальный хост (пользовательский типизированный хост) без порта, и он почти надежен:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню