Was ist der Unterschied zwischen HTTP_HOST und SERVER_NAME in PHP?

533

Was ist der Unterschied zwischen HTTP_HOSTund SERVER_NAMEin PHP?

wo:

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

Wann würden Sie in Betracht ziehen, übereinander zu verwenden und warum?

Emanuil Rusev
quelle
14
"Normalerweise greife ich zu HTTP_HOST, damit der Benutzer auf dem genauen Hostnamen bleibt, auf dem er gestartet wurde. Wenn ich beispielsweise dieselbe Site in einer .com- und .org-Domain habe, möchte ich niemanden von .org an senden .com, insbesondere wenn sie auf .org Login-Token haben, die sie verlieren würden, wenn sie an die andere Domain gesendet würden. " - Dies und einige andere interessante Punkte von stackoverflow.com/questions/1459739/…
Yarin
5
@Yarin, Vergiss nicht , die Ergebnisse vonHTTP_HOST auf der Whitelist zu überprüfen . Andernfalls kann ein Angreifer einen beliebigen Wert in die HTTP- Host:Anforderung eingeben und den Server dazu bringen, diesen zu akzeptieren.
Pacerier
6
Anfänger: Diese Frage bezieht sich auf Werte, die normalerweise über $_SERVER['HTTP_HOST']oder$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Antworten:

780

Das HTTP_HOSTwird aus dem HTTP-Anforderungsheader abgerufen und dies ist, was der Client tatsächlich als "Zielhost" der Anforderung verwendet hat. Das SERVER_NAMEist in der Serverkonfiguration definiert. Welche Sie verwenden, hängt davon ab, wofür Sie sie benötigen. Sie sollten jetzt jedoch erkennen, dass der eine ein vom Client gesteuerter Wert ist, der für die Verwendung in der Geschäftslogik möglicherweise nicht zuverlässig ist, und der andere ein vom Server gesteuerter Wert ist, der zuverlässiger ist. Sie müssen jedoch sicherstellen, dass der betreffende Webserver SERVER_NAMEkorrekt konfiguriert ist. Am Beispiel von Apache HTTPD finden Sie hier einen Auszug aus der Dokumentation :

Wenn no ServerNameangegeben ist, versucht der Server, den Hostnamen durch eine umgekehrte Suche der IP-Adresse abzuleiten. Wenn in der kein Port angegeben ist ServerName, verwendet der Server den Port aus der eingehenden Anforderung. Für eine optimale Zuverlässigkeit und Vorhersagbarkeit sollten Sie mithilfe der ServerNameDirektive einen expliziten Hostnamen und Port angeben .


Update : Nachdem Sie die Antwort von Pekka auf Ihre Frage überprüft haben, die einen Link zu Bobince's Antwort enthält , für die PHP immer HTTP_HOSTden Wert zurückgeben würde SERVER_NAME, widerspricht dies meinen eigenen Erfahrungen mit PHP 4.x + Apache HTTPD 1.2.x vor einigen Jahren Ich habe etwas Staub aus meiner aktuellen XAMPP-Umgebung unter Windows XP (Apache HTTPD 2.2.1 mit PHP 5.2.8) geblasen, es gestartet, eine PHP-Seite erstellt, die beide Werte druckt, eine Java-Testanwendung URLConnectionzum Ändern des HostHeaders und erstellt Tests haben mich gelehrt, dass dies tatsächlich (fälschlicherweise) der Fall ist.

Nachdem ich PHP zum ersten Mal vermutet und einige PHP-Fehlerberichte zu diesem Thema gelesen hatte, stellte ich fest, dass der Grund für das Problem im verwendeten Webserver liegt und dass der HTTP- HostHeader bei SERVER_NAMEAnforderung fälschlicherweise zurückgegeben wurde. Also habe ich mich mit verschiedenen Schlüsselwörtern zum Thema in Apache HTTPD-Fehlerberichte vertieft und schließlich einen verwandten Fehler gefunden . Dieses Verhalten wurde seit Apache HTTPD 1.3 eingeführt. Sie müssen die UseCanonicalNameAnweisung onim <VirtualHost>Eintrag des ServerNameIn festlegen httpd.conf(überprüfen Sie auch die Warnung am unteren Rand des Dokuments !).

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

Das hat bei mir funktioniert.

Zusammenfassend SERVER_NAMEist es zuverlässiger, aber Sie sind abhängig von der Serverkonfiguration!

BalusC
quelle
5
Okay, das löst mein Problem, das nicht mit dem OP zusammenhängt, aber relevant ist. Ich war sehr besorgt über Sicherheitsprobleme, indem ich alles verwendete, was ein Browser liefern konnte. Diese Antwort war eine riesige Hilfe. Vielen Dank, dass Sie sich die Zeit genommen haben, es zusammenzustellen.
Yitzhak
2
Warum ist HTTP_HOST Ihrer Meinung nach nicht zuverlässig? Ja, es wird vom Benutzer bereitgestellt, aber wenn der Benutzer einen falschen Wert angibt, gibt Ihre Serverkonfiguration automatisch 503 zurück und Ihr PHP-Skript wird nicht einmal ausgeführt!
Pacerier
1
@ Pacerier: Zum Zeitpunkt des Schreibens dieser Antwort war dies nicht der Fall. Versionen werden in der Antwort erwähnt. Ich halte nicht mehr mit PHP Schritt, daher kann ich nicht sagen, ob es sich tatsächlich in einer neueren Version geändert hat.
BalusC
2
Eine einfache Möglichkeit, Apache von WinXP aus zu täuschen, besteht darin, der 'Hosts'-Datei eine Zeile hinzuzufügen, die besagt, dass die IP des Servers einer anderen Domäne zugewiesen ist, wie folgt: "127.0.0.1 mydomain.com". Ich habe dies oft benutzt, um eine lokale Website zu zeigen, die mein Publikum dazu verleitet, zu glauben, dass ich eine Internetverbindung habe und die Website sehr schnell geladen wird. Sie könnten in die andere Richtung gehen und Apache mit "173.194.41.5 localhost" dazu verleiten, zu glauben, dass es lokal ausgeführt wird. Daher sollten Sie SERVER_NAME niemals vollständig vertrauen, es sei denn, Sie sind sicher, dass Ihr Apache gut konfiguriert ist.
Vicenteherrera
1
Ich möchte nur hinzufügen, dass NGINX + PHP-FPM den von der server_nameDirektive festgelegten Wert zurückgibt . Besonders wenn nein gesetzt server_nameist, _SERVER["SERVER_NAME"]wird auch leer sein.
white_gecko
69

HTTP_HOSTist der vom Client gesendete Zielhost. Es kann vom Benutzer frei manipuliert werden. Es ist kein Problem, eine Anfrage an Ihre Site zu senden, in der Sie nach einem HTTP_HOSTWert von gefragt werden www.stackoverflow.com.

SERVER_NAMEstammt aus der VirtualHostDefinition des Servers und wird daher als zuverlässiger angesehen. Es kann jedoch auch von außen unter bestimmten Bedingungen im Zusammenhang mit der Einrichtung Ihres Webservers manipuliert werden: Siehe diese SO-Frage , die sich mit den Sicherheitsaspekten beider Varianten befasst.

Sie sollten sich auch nicht darauf verlassen, um sicher zu sein. Das heißt, was zu verwenden ist, hängt wirklich davon ab, was Sie tun möchten. Wenn Sie feststellen möchten, auf welcher Domain Ihr Skript ausgeführt wird, können Sie diese sicher verwenden HTTP_HOST, solange ungültige Werte, die von einem böswilligen Benutzer stammen, nichts beschädigen können.

Pekka
quelle
8
Ja, aber eine Anfrage, die nach einem HTTP_HOST-Wert von www.stackoverflow.com fragt, wird von den meisten HTTP-Servern im Vorfeld abgelehnt, sodass das PHP-Skript die Anfrage nicht einmal sehen kann!
Pacerier
2
@Pacerier true, aber nicht immer, wenn der Server nicht richtig konfiguriert ist.
Pekka
1
Wie in BalusCs Beitrag erwähnt, enthalten beide Variablen beim Zugriff auf einen virtuellen Apache-Host über IP die IP (standardmäßig) und nicht den tatsächlichen Servernamen. Sie müssen UseCanonicalName onin httpd.conf verwenden, um zu erzwingen SERVER_NAME, dass der tatsächliche Servername angegeben wird.
Simon East
@ Pekka 웃, Wenn der Server nicht richtig konfiguriert ist, $_SERVER['SERVER_NAME']würde er nicht so gut funktionieren . Ein schlecht konfigurierter Server wird $_SERVER['SERVER_NAME']basierend auf dem Wert der Clientanforderung festgelegt Host:. Beide sind gleich.
Pacerier
Gute Antwort, aber ich würde kein virtuelles Hosting annehmen.
Anthony Rutledge
55

Wie in dieser Antwort erwähnt , enthält der Server den Port, wenn er auf einem anderen Port als 80 ausgeführt wird (wie dies auf einem Entwicklungs- / Intranetcomputer üblich ist) HTTP_HOST, während SERVER_NAMEdies nicht der Fall ist.

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

(Zumindest ist mir das bei Apache-Port-basierten virtuellen Hosts aufgefallen.)

Beachten Sie, dass HTTP_HOSTdies nicht enthalten ist, :443wenn Sie unter HTTPS ausgeführt werden (es sei denn, Sie verwenden einen nicht standardmäßigen Port, den ich nicht getestet habe).

Wie andere angemerkt haben, unterscheiden sich die beiden auch bei der Verwendung von IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Simon East
quelle
2
Wann werden sie dieses heimtückische Verhalten beheben?
Pacerier
27

Bitte beachten Sie, dass Sie IPv6 wahrscheinlich verwenden möchten, wenn Sie IPv6 verwenden möchten HTTP_HOST eher als verwendenSERVER_NAME . Wenn Sie http://[::1]/die Umgebungsvariablen eingeben , sind folgende:

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

Dies bedeutet, dass Sie möglicherweise ein unangenehmes Ergebnis erhalten, wenn Sie beispielsweise einen mod_rewrite ausführen. Beispiel für eine SSL-Umleitung:

# 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}/

Dies gilt NUR, wenn Sie ohne Hostnamen auf den Server zugreifen.

Daniel Marschall
quelle
1
SiteGround, in ihrem internen http zu https Redirect Code, verwendenhttps://%{SERVER_NAME}%{REQUEST_URI}
IXN
6

Wenn Sie eine server.php oder was auch immer überprüfen möchten, möchten Sie sie wie folgt aufrufen:

<?php
    phpinfo(INFO_VARIABLES);
?>

oder

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

    print_r($_SERVER);
?>

Greifen Sie dann mit allen gültigen URLs für Ihre Website darauf zu und überprüfen Sie den Unterschied.

stevewh
quelle
5

Kommt darauf an, was ich herausfinden will. SERVER_NAME ist der Hostname des Servers, während HTTP_HOST der virtuelle Host ist, mit dem der Client verbunden ist.

Rowland Shaw
quelle
4
Nicht genau wahr Rowland, SERVER_NAMEist normalerweise der Name des VirtualHost, nicht der Server selbst. Und in Apache SERVER_NAMEwird häufig mit dem gleichen Wert wie HTTP_HOST(siehe BalusCs Antwort) gefüllt.
Simon East
1
@ Simon, da die meisten Hosts jetzt VirtualHost sind, was würden Sie mit dem Namen "der Server selbst" meinen?
Pacerier
Wenn Sie einen Virtual Private Server (VPS) mit einer Website ausführen, müssen Sie nicht davon ausgehen, dass dies SERVER_NAMEfür einen virtuellen Host gilt. Es kann jedoch weiterhin ein virtuelles Host-Setup für einen Standort verwendet werden. Viele Leute nutzen Shared Hosting, also verstehe ich Ihren Standpunkt.
Anthony Rutledge
2

Ich habe eine Weile gebraucht, um zu verstehen, was Menschen unter " SERVER_NAMEzuverlässiger" verstehen . Ich verwende einen gemeinsam genutzten Server und habe keinen Zugriff auf Anweisungen für virtuelle Hosts. Also benutze ich mod_rewrite in .htaccess , um verschiedene HTTP_HOSTs verschiedenen Verzeichnissen zuzuordnen . In diesem Fall ist es HTTP_HOSTsinnvoll.

Ähnlich verhält es sich, wenn man namenbasierte virtuelle Hosts verwendet: Die ServerNameDirektive innerhalb eines virtuellen Hosts gibt lediglich an, welcher Hostname diesem virtuellen Host zugeordnet wird. Unter dem Strich muss in beiden Fällen der vom Client während der Anfrage ( HTTP_HOST) angegebene Hostname mit einem Namen innerhalb des Servers abgeglichen werden, der selbst einem Verzeichnis zugeordnet ist. Ob die Zuordnung mit virtuellen Host-Direktiven oder mit htaccess mod_rewrite-Regeln erfolgt, ist hier zweitrangig. In diesen Fällen HTTP_HOSTwird das gleiche sein wie SERVER_NAME. Ich bin froh, dass Apache so konfiguriert ist.

Bei IP-basierten virtuellen Hosts ist die Situation jedoch anders. In diesem Fall und nur in diesem Fall SERVER_NAMEund HTTP_HOSTkann unterschiedlich sein, da der Client den Server jetzt anhand der IP-Adresse und nicht anhand des Namens auswählt. In der Tat kann es spezielle Konfigurationen geben, bei denen dies wichtig ist.

Ab sofort werde ich also verwenden SERVER_NAME, nur für den Fall, dass mein Code in diesen speziellen Konfigurationen portiert wird.

Dominic108
quelle
2

Angenommen, man hat ein einfaches Setup (CentOS 7, Apache 2.4.x und PHP 5.6.20) und nur eine Website (ohne virtuelles Hosting) ...

Im PHP-Sinne $_SERVER['SERVER_NAME']ist ein Element, das PHP im $_SERVERSuperglobal registriert, basierend auf Ihrer Apache-Konfiguration ( **ServerName**Direktive mit UseCanonicalName On) in httpd.conf (sei es aus einer enthaltenen Konfigurationsdatei des virtuellen Hosts, was auch immer usw.). HTTP_HOST wird vom HTTP abgeleitethost Header abgeleitet. Behandeln Sie dies als Benutzereingabe. Filtern und validieren Sie vor der Verwendung.

Hier ist ein Beispiel, wo ich $_SERVER['SERVER_NAME']als Grundlage für einen Vergleich verwende. Die folgende Methode stammt aus einer konkreten untergeordneten Klasse, die ich mit dem Namen ServerValidator(child of Validator) erstellt habe. ServerValidatorÜberprüft sechs oder sieben Elemente in $ _SERVER, bevor sie verwendet werden.

Um festzustellen, ob die HTTP-Anforderung POST ist, verwende ich diese Methode.

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

Zum Zeitpunkt des Aufrufs dieser Methode wären alle Filter und Validierungen der relevanten $ _SERVER-Elemente erfolgt (und die relevanten Eigenschaften wurden festgelegt).

Die Linie ...

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

... prüft, ob der $_SERVER['HTTP_HOST']Wert (der letztendlich aus dem angeforderten hostHTTP-Header abgeleitet wurde) übereinstimmt $_SERVER['SERVER_NAME'].

Nun, ich bin mit Superglobal sprechen , mein Beispiel zu erklären, aber das ist nur , weil einige Menschen sind nicht vertraut mit INPUT_GET, INPUT_POSTund INPUT_SERVERin Bezug auf filter_input_array().

Unter dem Strich verarbeite ich POST-Anfragen auf meinem Server nur, wenn alle vier Bedingungen erfüllt sind. In Bezug auf POST-Anforderungen bedeutet das Versagen der Bereitstellung eines HTTP- hostHeaders (auf frühere Tests getestet) für strenge HTTP 1.0- Browser das Schicksal . Darüber hinaus ist der angeforderte Host muss mit dem Wert übereinstimmen für in der httpd.conf , und durch Erweiterung, um den Wert für die in der Superglobal. Wieder würde ich mit den PHP-Filterfunktionen verwenden, aber Sie fangen meine Drift.ServerName$_SERVER('SERVER_NAME')$_SERVERINPUT_SERVER

Beachten Sie, dass Apache häufig ServerNamein Standardumleitungen verwendet wird (z. B. wenn der nachfolgende Schrägstrich von einer URL entfernt wird: Beispiel: http://www.foo.com wird zu http://www.foo.com/ ), auch wenn dies nicht der Fall ist Verwenden der URL-Umschreibung.

Ich benutze $_SERVER['SERVER_NAME']als Standard nicht $_SERVER['HTTP_HOST']. Zu diesem Thema gibt es viel Hin und Her. $_SERVER['HTTP_HOST']könnte leer sein, daher sollte dies nicht die Grundlage für die Erstellung von Codekonventionen wie meiner oben beschriebenen öffentlichen Methode sein. Nur weil beide festgelegt werden können, ist dies jedoch keine Garantie dafür, dass sie gleich sind. Testen ist der beste Weg, um sicher zu wissen (unter Berücksichtigung der Apache-Version und der PHP-Version).

Anthony Rutledge
quelle
0

Wie balusC sagte, ist SERVER_NAME nicht zuverlässig und kann in der Apache-Konfiguration, der Servernamen-Konfiguration des Servers und der Firewall geändert werden, die sich zwischen Ihnen und dem Server befinden können.

Die folgende Funktion gibt immer einen echten Host (vom Benutzer eingegebener Host) ohne Port zurück und ist fast zuverlässig:

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

$ _SERVER ['SERVER_NAME'] basiert auf der Konfiguration Ihres Webservers. $ _SERVER ['HTTP_HOST'] basiert auf der Anfrage des Clients.

Vitalie
quelle