Jaka jest różnica między HTTP_HOST a SERVER_NAME w PHP?

533

Jaka jest różnica między PHP HTTP_HOSTa SERVER_NAMEPHP?

gdzie:

  • HTTP_POST === $_SERVER['HTTP_HOST']
  • SERVER_NAME === $_SERVER['SERVER_NAME']

Kiedy rozważasz użycie jednego nad drugim i dlaczego?

Emanuil Rusev
źródło
14
„Zwykle wybieram HTTP_HOST, aby użytkownik pozostawał na dokładnej nazwie hosta, na której zaczął. Na przykład jeśli mam tę samą witrynę w domenie .com i .org, nie chcę wysyłać kogoś z domeny .org do .com, szczególnie jeśli mogą mieć tokeny logowania na .org, które utracą, jeśli zostaną wysłane do innej domeny. ” - To i kilka innych interesujących punktów z stackoverflow.com/questions/1459739/…
Yarin
5
@Yarin, Nie zapomnij zweryfikować białej listy wynikówHTTP_HOST . W przeciwnym razie osoba atakująca może wprowadzić dowolną wartość w Host:żądaniu HTTP i zmusić serwer do jej zaakceptowania.
Pacerier
6
Początkujący: To pytanie odnosi się do wartości zwykle uzyskiwanych przez $_SERVER['HTTP_HOST']lub$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Odpowiedzi:

780

HTTP_HOSTJest otrzymywany z nagłówka żądania HTTP i to, co klient faktycznie wykorzystywane jako „host docelowy” wniosku. SERVER_NAMEJest zdefiniowany w konfiguracji serwera. To, którego użyć, zależy od tego, do czego go potrzebujesz. Powinieneś jednak teraz zdać sobie sprawę, że jedna jest wartością kontrolowaną przez klienta, która może dlatego nie być wiarygodna do zastosowania w logice biznesowej, a druga jest wartością kontrolowaną przez serwer, która jest bardziej niezawodna. Musisz jednak upewnić się, że dany serwer WWW ma SERVER_NAMEpoprawnie skonfigurowany. Biorąc przykład Apache HTTPD, oto fragment jego dokumentacji :

Jeśli nie ServerNameokreślono, serwer próbuje wydedukować nazwę hosta, wykonując wyszukiwanie wsteczne adresu IP. Jeśli nie podano żadnego portu ServerName, serwer użyje portu z przychodzącego żądania. Aby uzyskać optymalną niezawodność i przewidywalność, należy określić jawną nazwę hosta i port za pomocą ServerNamedyrektywy.


Aktualizacja : po sprawdzeniu odpowiedzi Pekki na twoje pytanie, która zawiera link do odpowiedzi bobince'a, za którą PHP zawsze zwróci HTTP_HOSTwartość SERVER_NAME, co jest sprzeczne z moimi własnymi doświadczeniami PHP 4.x + Apache HTTPD 1.2.x sprzed kilku lat , Zdmuchnąłem trochę kurzu z mojego obecnego środowiska XAMPP w systemie Windows XP (Apache HTTPD 2.2.1 z PHP 5.2.8), uruchomiłem go, utworzyłem stronę PHP, która drukuje obie wartości, stworzyłem aplikację testową Java URLConnectiondo modyfikacji Hostnagłówka i testy nauczyły mnie, że tak jest rzeczywiście (niepoprawnie).

Po pierwszym podejrzeniu PHP i przejrzeniu niektórych raportów o błędach PHP dotyczących tego tematu, dowiedziałem się, że źródłem problemu jest używany serwer sieciowy i że niepoprawnie zwrócił Hostnagłówek HTTP, gdy SERVER_NAMEzostał o to poproszony. Więc zagłębiłem się w raporty błędów Apache HTTPD przy użyciu różnych słów kluczowych dotyczących tematu i w końcu znalazłem pokrewny błąd . To zachowanie zostało wprowadzone od około Apache HTTPD 1.3. Trzeba ustawić UseCanonicalNamedyrektywy onw <VirtualHost>wpisu ServerNameIN httpd.conf(również sprawdzić ostrzeżenia na dole dokumentu !).

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

To zadziałało dla mnie.

Podsumowując, SERVER_NAMEjest bardziej niezawodny, ale jesteś zależny od konfiguracji serwera!

BalusC
źródło
5
Ok, to rozwiązuje mój problem, który nie jest związany z PO, ale jest istotny. Bardzo martwiłem się kwestiami bezpieczeństwa, używając wszystkiego, co przeglądarka mogła dostarczyć. Ta odpowiedź była OGROMNĄ pomocą. Dziękujemy za poświęcenie czasu na złożenie go w całość.
Icchak
2
Dlaczego mówisz, że HTTP_HOST nie jest niezawodny? Tak, jest dostarczany przez użytkownika, ale jeśli użytkownik poda jakąś fałszywą wartość, konfiguracja serwera automatycznie zwróci 503, a skrypt PHP nawet nie zostanie uruchomiony!
Pacerier
1
@Pacerier: w momencie pisania tej odpowiedzi tak nie było. Wersje są wymienione w odpowiedzi. Nie nadążam już za PHP, więc nie mogę powiedzieć, czy rzeczywiście zmieniła się w nowszej wersji.
BalusC
2
Prostym sposobem na oszukanie Apache z WinXP jest dodanie wiersza do pliku „hosts” stwierdzającego, że adres IP serwera jest przypisany do innej domeny, na przykład: „127.0.0.1 mojadomena.com”. Używałem tego wiele razy, aby pokazać lokalną stronę internetową, która oszukuje moich odbiorców, aby myśleć, że mam połączenie internetowe i witrynę ładuje się bardzo szybko. Możesz pójść w drugą stronę i oszukać Apache, aby pomyślał, że działa lokalnie z „173.194.41.5 localhost”, więc nigdy nie powinieneś w pełni ufać SERVER_NAME, chyba że masz pewność, że twój Apache jest dobrze skonfigurowany.
vicenteherrera
1
Chcę tylko dodać, że NGINX + PHP-FPM zwraca wartość ustawioną przez server_namedyrektywę. Zwłaszcza jeśli nie server_namejest ustawione, również _SERVER["SERVER_NAME"]będzie puste.
white_gecko
69

HTTP_HOSTto host docelowy wysłany przez klienta. Użytkownik może nim dowolnie manipulować. Nie ma problemu z wysłaniem żądania do Twojej witryny z prośbą o HTTP_HOSTwartość www.stackoverflow.com.

SERVER_NAMEpochodzi z VirtualHostdefinicji serwera i dlatego jest uważany za bardziej niezawodny. Można jednak nim również manipulować z zewnątrz pod pewnymi warunkami związanymi z konfiguracją serwera WWW: zobacz To pytanie SO, które dotyczy aspektów bezpieczeństwa obu odmian.

Nie powinieneś polegać na żadnym z nich, aby być bezpiecznym. To powiedziawszy, to, czego użyć naprawdę zależy od tego, co chcesz zrobić. Jeśli chcesz ustalić, na której domenie działa skrypt, możesz bezpiecznie korzystać, HTTP_HOSTdopóki niepoprawne wartości pochodzące od złośliwego użytkownika nie mogą niczego zepsuć.

Pekka
źródło
8
Tak, ale prośba o podanie wartości HTTP_HOST www.stackoverflow.com zostanie odrzucona przez większość serwerów HTTP z góry, więc skrypt PHP nawet nie zobaczy żądania!
Pacerier
2
@Pacerier prawda, ale nie zawsze, jeśli serwer nie jest poprawnie skonfigurowany.
Pekka
1
Jak wspomniano w poście BalusC, kiedy uzyskujesz dostęp do wirtualnego hosta Apache przez IP, obie te zmienne zawierają IP (domyślnie), a nie rzeczywistą nazwę serwera. Musisz użyć UseCanonicalName onw httpd.conf, aby wymusić SERVER_NAMErzeczywistą nazwę serwera.
Simon East
@Pekka 웃, Jeśli serwer nie jest poprawnie skonfigurowany, $_SERVER['SERVER_NAME']nie działałby również . Źle skonfigurowany serwer ustawi się $_SERVER['SERVER_NAME']na podstawie wartości Host:żądania klienta . Oba są równe.
Pacerier
Dobra odpowiedź, ale nie zakładałbym wirtualnego hostingu.
Anthony Rutledge
55

Jak wspomniałem w tej odpowiedzi , jeśli serwer działa na porcie innym niż 80 (co może być powszechne na komputerze deweloperskim / intranetowym), wówczas HTTP_HOSTzawiera port, podczas gdy SERVER_NAMEnie.

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

(Przynajmniej tak zauważyłem w wirtualnych hostach opartych na portach Apache)

Należy pamiętać, że HTTP_HOSTma nie zawierać :443, gdy działa na HTTPS (chyba że działa na niestandardowym porcie, które nie zostały przetestowane).

Jak zauważyli inni, oba różnią się także przy korzystaniu z IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Simon East
źródło
2
Kiedy naprawią to podstępne zachowanie?
Pacerier
27

Pamiętaj, że jeśli chcesz korzystać z IPv6, prawdopodobnie HTTP_HOSTraczej chcesz go używać SERVER_NAME. Jeśli wpiszesz http://[::1]/zmienne środowiskowe, będą to:

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

Oznacza to, że jeśli wykonasz mod_rewrite, możesz uzyskać nieprzyjemny wynik. Przykład przekierowania 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}/

Dotyczy to TYLKO jeśli uzyskasz dostęp do serwera bez nazwy hosta.

Daniel Marschall
źródło
1
SiteGround, w swoim wewnętrznym http do https https, przekieruj kod, użyjhttps://%{SERVER_NAME}%{REQUEST_URI}
IXN
6

Jeśli chcesz sprawdzić przez server.php lub cokolwiek innego, chcesz wywołać go w następujący sposób:

<?php
    phpinfo(INFO_VARIABLES);
?>

lub

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

    print_r($_SERVER);
?>

Następnie uzyskaj dostęp do wszystkich prawidłowych adresów URL swojej witryny i sprawdź różnicę.

Stevewh
źródło
5

Zależy, co chcę się dowiedzieć. SERVER_NAME to nazwa hosta serwera, podczas gdy HTTP_HOST to wirtualny host, z którym klient się łączy.

Rowland Shaw
źródło
4
Nie do końca prawda Rowland, SERVER_NAMEto zwykle nazwa VirtualHost, a nie sam serwer. A w Apache SERVER_NAMEjest często zapełniany tą samą wartością co HTTP_HOST(patrz odpowiedź BalusC).
Simon East
1
@ Simon, skoro większość hostów jest teraz VirtualHost, co byś rozumiał pod nazwą „sam serwer”?
Pacerier
Jeśli prowadzisz wirtualny prywatny serwer (VPS) z jedną witryną, nie musisz zakładać, że SERVER_NAMEdotyczy to wirtualnego hosta. Jednak nadal można użyć konfiguracji hosta wirtualnego dla jednej witryny. Wiele osób korzysta z hostingu współdzielonego, więc rozumiem twój punkt widzenia.
Anthony Rutledge
2

Trochę czasu zajęło mi zrozumienie, co ludzie rozumieją przez „ SERVER_NAMEjest bardziej niezawodny”. Używam serwera współdzielonego i nie mam dostępu do dyrektyw hosta wirtualnego. Tak więc używam mod_rewrite w .htaccess do mapowania różnych HTTP_HOSTs do różnych katalogów. W takim przypadku ma HTTP_HOSTto sens.

Sytuacja jest podobna, jeśli używa się hostów wirtualnych opartych na nazwach: ServerNamedyrektywa w hoście wirtualnym mówi po prostu, która nazwa hosta zostanie zmapowana na tym hoście wirtualnym. Najważniejsze jest to, że w obu przypadkach HTTP_HOSTnazwa hosta podana przez klienta podczas request ( ) musi być dopasowana do nazwy na serwerze, która sama jest odwzorowana na katalog. Niezależnie od tego, czy mapowanie odbywa się za pomocą dyrektyw hosta wirtualnego, czy za pomocą htaccess, reguły mod_rewrite mają tutaj znaczenie wtórne. W takich przypadkach HTTP_HOSTbędzie taki sam jak SERVER_NAME. Cieszę się, że Apache jest skonfigurowany w ten sposób.

Sytuacja wygląda jednak inaczej w przypadku hostów wirtualnych opartych na protokole IP. W takim przypadku i tylko w tym przypadku, SERVER_NAMEa HTTP_HOSTmoże być inaczej, bo teraz klient wybiera serwer przez IP, a nie nazwy. Rzeczywiście mogą istnieć specjalne konfiguracje, w których jest to ważne.

Tak więc, od teraz, użyję SERVER_NAME, na wypadek, gdyby mój kod został przeniesiony w tych specjalnych konfiguracjach.

Dominic108
źródło
2

Zakładając, że masz prostą konfigurację (CentOS 7, Apache 2.4.x i PHP 5.6.20) i tylko jedną stronę internetową (nie zakładając wirtualnego hostingu) ...

W sensie PHP $_SERVER['SERVER_NAME']jest elementem, który PHP rejestruje w $_SERVERsuperglobalu na podstawie konfiguracji Apache ( **ServerName**dyrektywy z UseCanonicalName On) w httpd.conf (czy to z dołączonego pliku konfiguracji hosta wirtualnego, cokolwiek, itp.). HTTP_HOST pochodzi z hostnagłówka HTTP . Traktuj to jako dane wprowadzone przez użytkownika. Filtruj i sprawdzaj przed użyciem.

Oto przykład, w którym wykorzystuję $_SERVER['SERVER_NAME']jako podstawę porównania. Poniższa metoda pochodzi z konkretnej klasy potomnej, którą nazwałem ServerValidator(child of Validator). ServerValidatorsprawdza sześć lub siedem elementów w $ _SERVER przed ich użyciem.

Przy określaniu, czy żądanie HTTP jest POST, używam tej metody.

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

Do czasu wywołania tej metody nastąpiłoby całe filtrowanie i sprawdzanie poprawności odpowiednich elementów $ _SERVER (i odpowiednich właściwości).

Linia ...

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

... sprawdza, czy $_SERVER['HTTP_HOST']wartość (ostatecznie uzyskana z żądanego hostnagłówka HTTP) jest zgodna $_SERVER['SERVER_NAME'].

Teraz używam superglobalną mówić wyjaśnić moim przykładem, ale to tylko dlatego, że niektórzy ludzie nie znają INPUT_GET, INPUT_POSToraz INPUT_SERVERw odniesieniu do filter_input_array().

Najważniejsze jest to, że nie obsługuję żądań POST na moim serwerze, chyba że zostaną spełnione wszystkie cztery warunki. W związku z tym, jeśli chodzi o żądania POST, brak dostarczenia hostnagłówka HTTP (przetestowanego pod kątem obecności wcześniej) oznacza zgubę dla ścisłych przeglądarek HTTP 1.0 . Ponadto zwróciła gospodarz musi odpowiadać wartości dla ServerNamew httpd.conf , a za przedłużeniem, wartość $_SERVER('SERVER_NAME')w $_SERVERsuperglobalną. Znowu używałbym INPUT_SERVERz funkcjami filtrowania PHP, ale łapiesz mój dryf.

Należy pamiętać, że Apache często używa ServerNamesię standardowych przekierowań (takie jak pozostawiając ukośnik off URL: przykład http://www.foo.com staje http://www.foo.com/ ), nawet jeśli nie są za pomocą przepisywania adresów URL.

Używam $_SERVER['SERVER_NAME']jako standardu, nie $_SERVER['HTTP_HOST']. W tej kwestii jest wiele do przodu i do tyłu. $_SERVER['HTTP_HOST']może być pusty, więc nie powinno to stanowić podstawy do tworzenia konwencji kodu, takich jak powyższa metoda publiczna. Ale tylko dlatego, że oba mogą być ustawione, nie gwarantuje, że będą równe. Testowanie jest najlepszym sposobem, aby wiedzieć na pewno (mając na uwadze wersję Apache i wersję PHP).

Anthony Rutledge
źródło
0

Jak powiedział balusC, SERVER_NAME nie jest niezawodny i można go zmienić w konfiguracji apache, konfiguracji nazwy serwera i zaporze ogniowej, która może znajdować się między tobą a serwerem.

Następująca funkcja zawsze zwraca prawdziwy host (host wpisany przez użytkownika) bez portu i jest prawie niezawodny:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}
MSS
źródło
0

$ _SERVER [„SERVER_NAME”] jest oparty na konfiguracji serwerów internetowych. $ _SERVER ['HTTP_HOST'] jest oparty na żądaniu klienta.

Witalij
źródło