PHP $ SERVER ['HTTP HOST'] vs. $ SERVER ['SERVER NAME'], est-ce que je comprends les pages de manuel correctement?

j'ai fait beaucoup de recherche et j'ai aussi lu le PHP $_SERVER docs . Ai-je le droit d'utiliser pour mes scripts PHP des définitions de liens simples utilisées dans tout mon site?

$_SERVER['SERVER_NAME'] est basé sur le fichier de configuration de votre serveur web (Apache2 dans mon cas), et varie en fonction de quelques directives: (1) VirtualHost, (2) ServerName, (3) UseCanonicalName, etc.

$_SERVER['HTTP_HOST'] est fondé sur la demande de client.

par conséquent, il me semble que le bon à utiliser pour rendre mes scripts aussi compatibles que possible serait $_SERVER['HTTP_HOST'] . Cette hypothèse est correcte?

commentaires suivants:

je suppose que je suis devenu un peu paranoïaque après avoir lu cet article et en notant que certaines personnes ont dit "ils ne feraient confiance à aucun des $_SERVER vars":

apparemment, la discussion porte principalement sur $_SERVER['PHP_SELF'] et sur la raison pour laquelle vous ne devriez pas l'utiliser dans l'attribut form action s'échapper pour prévenir les attaques XSS.

ma conclusion à propos de ma question initiale ci-dessus est qu'il est" sûr "d'utiliser $_SERVER['HTTP_HOST'] pour tous les liens sur un site sans avoir à se soucier des attaques XSS, même lorsqu'il est utilisé dans les formulaires.

corrigez-moi si je me trompe.

140
demandé sur Jeff 2009-09-22 16:16:47

8 réponses

c'est probablement la première pensée de tout le monde. Mais c'est un peu plus difficile. Voir article de Chris Shiflett SERVER_NAME Versus HTTP_HOST .

il semble qu'il n'y ait pas de balle en argent. Ce n'est que lorsque vous forcez Apache à utiliser le nom canonique que vous obtiendrez toujours le bon nom de serveur avec SERVER_NAME .

Si vous allez ou vous vérifier l'hôte nom contre une liste blanche:

$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;
}
129
répondu Gumbo 2009-09-22 12:54:54

juste une note supplémentaire - si le serveur tourne sur un port autre que 80 (comme cela peut être courant sur une machine de développement/intranet) alors HTTP_HOST contient le port, tandis que SERVER_NAME ne le contient pas.

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

(du moins c'est ce que j'ai remarqué dans Apache port-based virtualhosts)

Comme Mike l'a noté ci-dessous, HTTP_HOST ne pas contenir :443 lors de l'exécution sur HTTPS (sauf si vous êtes en cours d'exécution sur un port non standard, que je n'ai pas testé).

59
répondu Simon East 2013-10-11 07:17:45

il s'agit d'une traduction verbeuse de ce que Symfony utilise pour obtenir le nom d'hôte ( voir le deuxième exemple pour une traduction plus littérale ):

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);
}

périmé:

c'est ma traduction pour dépouiller PHP d'une méthode utilisée dans le cadre de Symfony qui essaie d'obtenir le nom d'hôte de toutes les façons possibles dans l'ordre des meilleures pratiques:

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);
}
23
répondu antitoxic 2014-07-25 09:39:00

utiliser l'un ou l'autre. Ils sont tous les deux également (in)sécurisés, car dans de nombreux cas SERVER_NAME est simplement peuplé à partir de HTTP_HOST de toute façon. Normalement, J'opte pour HTTP_HOST, de sorte que l'utilisateur reste sur le nom exact de l'hôte sur lequel il a démarré. Par exemple, si j'ai le même site sur un .com and .domaine org, Je ne veux pas envoyer quelqu'un de .org to .com, surtout s'ils ont des clés de connexion activées .org qu'ils perdraient s'ils étaient envoyés à l'autre domaine.

de toute façon, vous devez juste être sûr que votre webapp ne jamais répondre pour les bonnes domaines. Cela peut être fait (A) avec une vérification côté application comme Gumbo's, ou (b) en utilisant un hôte virtuel sur le(S) nom (s) de domaine que vous voulez que ne répond pas aux requêtes qui donnent un en-tête D'hôte inconnu.

la raison en est que si vous autorisez l'accès à votre site sous n'importe quel ancien nom, vous vous exposez à des attaques de rebobinage DNS (où le nom d'hôte d'un autre site pointe vers votre IP, un utilisateur accède à votre site avec le nom d'hôte de l'attaquant, puis le nom d'hôte est déplacé à L'adresse IP de l'attaquant, en prenant vos cookies/auth avec elle) et le détournement de moteur de recherche (où un attaquant pointe leur propre nom d'hôte sur votre site et tente de faire des moteurs de recherche de le voir comme le "meilleur" nom d'hôte primaire).

apparemment, la discussion porte principalement sur $_SERVER ['PHP_SELF'] et pourquoi vous ne devriez pas l'utiliser dans l'attribut d'action form sans échapper à prévenir les attaques XSS.

Pfft. Eh bien , vous ne devriez pas utiliser tout dans tout attribut sans échapper avec htmlspecialchars($string, ENT_QUOTES) , donc il n'y a rien de spécial sur les variables de serveur là-bas.

21
répondu bobince 2009-09-22 17:24:34

la principale différence entre les deux est que $_SERVER['SERVER_NAME'] est une variable contrôlée par le serveur, tandis que $_SERVER['HTTP_HOST'] est une valeur contrôlée par l'utilisateur.

la règle de base est de ne jamais faire confiance aux valeurs de l'utilisateur, donc $_SERVER['SERVER_NAME'] est le meilleur choix.

comme Gumbo l'a fait remarquer, Apache construira SERVER_NAME à partir des valeurs fournies par l'utilisateur si vous ne définissez pas UseCanonicalName On .

Edit: après avoir dit tout cela, si le site utilise un serveur virtuel basé sur le nom, L'en-tête HTTP Host est le seul moyen d'atteindre les sites qui ne sont pas le site par défaut.

7
répondu Powerlord 2011-11-10 11:44:39

Est-il "bon" usage $_SERVER['HTTP_HOST'] pour tous les liens sur un site sans avoir à se soucier des attaques XSS, même lorsqu'il est utilisé dans les formulaires?

Oui, c'est safe utiliser $_SERVER['HTTP_HOST'] (et même $_GET et $_POST ) aussi longtemps que vous le vérifier eux avant de les accepter. C'est ce que je fais pour les serveurs de production sécurisés:

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
$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!';
// ...

L'avantage de $_SERVER['HTTP_HOST'] , c'est que son comportement est plus défini que $_SERVER['SERVER_NAME'] . Contraste ➫➫ :

contenu de L'en-tête Host: de la requête courante, s'il y en a une.

avec:

le nom de l'hôte du serveur sous lequel le script courant est exécuté.

utilisant une interface mieux définie comme $_SERVER['HTTP_HOST'] signifie que plus de SAPIs va mettre en œuvre en utilisant fiable comportement bien défini. (Contrairement à l'autre .) Toutefois, elle est encore totalement dépendante du SAPI. ➫➫ :

il n'y a aucune garantie que chaque serveur web fournira l'une de ces [ $_SERVER entrées]; serveurs peuvent omettre certains, ou fournir d'autres non énumérés ici.

Pour comprendre comment récupérer correctement le nom d'hôte, d'abord et avant tout vous devez comprendre qu'un serveur qui contient seulement code n'a aucun moyen de savoir (pré-requis pour vérifier) son propre nom sur le réseau. Il doit s'interfacer avec un composant qui lui fournit son propre nom. Cela peut être fait via:

  • fichier de configuration local

  • base de données locale

  • codée en dur dans le code source

  • demande externe ( curl )

  • client/de l'attaquant Host: demande

  • etc

généralement son fait via le fichier de configuration local (SAPI). Notez que vous l'avez configuré correctement, par exemple En Apache ➫➫ :

plusieurs choses doivent être "simulées" pour que l'hôte virtuel dynamique ressemble à un hôte normal.

le plus important est le nom du serveur qui est utilisé par Apache pour générer des URLs auto-référentielles, etc. Il est configuré avec la directive ServerName , et il est disponible pour CGIs via la variable d'environnement SERVER_NAME .

la valeur réelle utilisé au moment de l'exécution est contrôlé par le paramètre UseCanonicalName.

avec UseCanonicalName Off le nom du serveur provient du contenu de l'en-tête Host: de la requête. avec UseCanonicalName DNS il provient d'une recherche DNS inversée de l'adresse IP de l'hôte virtuel. Le premier paramètre est utilisé pour l'hébergement virtuel dynamique basé sur le nom, et le second est utilisé pour l'hébergement basé sur** IP.

si Apache ne peut pas trouver le nom du serveur parce qu'il n'y a pas d'en-tête Host: ou que la recherche DNS échoue alors la valeur configurée avec ServerName est utilisée à la place.

5
répondu Pacerier 2017-05-23 12:26:05

Je ne suis pas sûr et pas vraiment confiance $_SERVER['HTTP_HOST'] parce qu'il dépend de l'en-tête du client. D'une autre manière, si un domaine demandé par le client n'est pas le mien, ils ne vont pas entrer dans mon site parce que le DNS et le protocole TCP/IP le pointent vers la bonne destination. Cependant, je ne sais pas s'il est possible de détourner le DNS, le réseau ou même le serveur Apache. Pour être sûr, je définis le nom de l'hôte dans l'environnement et je le compare avec $_SERVER['HTTP_HOST'] .

ajouter SetEnv MyHost domain.com dans .htaccess fichier sur root et ajouter le code ths en commun.php

if (getenv('MyHost')!=$_SERVER['HTTP_HOST']) {
  header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
  exit();
}

j'inclus ce Commun.fichier php dans chaque page php. Cette page fait tout ce qui est nécessaire pour chaque requête comme session_start() , modifie le cookie de session et rejette si la méthode post vient d'un domaine différent.

2
répondu CallMeLaNN 2010-03-15 01:08:25

XSS sera toujours là même si vous utilisez $_SERVER['HTTP_HOST'] , $_SERVER['SERVER_NAME'] ou $_SERVER['PHP_SELF']

1
répondu Jaydeep Dave 2014-01-10 10:06:25