PHP $_SERVER[‘HTTP_HOST’] vs. $_SERVER[‘SERVER_NAME’], begrijp ik de man-pagina’s goed?

Ik heb veel gezocht en ook de PHP $_SERVER-documentengelezen. Heb ik dit recht om te gebruiken voor mijn PHP-scripts voor eenvoudige linkdefinities die op mijn site worden gebruikt?

$_SERVER['SERVER_NAME']is gebaseerd op het configuratiebestand van uw webserver (Apache2 in mijn geval), en varieert afhankelijk van een paar richtlijnen: (1) VirtualHost, (2) ServerName, (3) UseCanonicalName, enz.

$_SERVER['HTTP_HOST']is gebaseerd op het verzoek van de klant.

Daarom lijkt het mij dat $_SERVER['HTTP_HOST']de juiste is om te gebruiken om mijn scripts zo compatibel mogelijk te maken. Is deze veronderstelling juist?

Vervolgopmerkingen:

Ik denk dat ik een beetje paranoïde werd na het lezen van dit artikel en merkte op dat sommige mensen zeiden “ze zouden geen van de $_SERVERvars vertrouwen”:

Blijkbaar gaat de discussie vooral over $_SERVER['PHP_SELF']en waarom je het niet in het form action-attribuut zou moeten gebruiken zonder de juiste escaping om XSS-aanvallen te voorkomen.

Mijn conclusie over mijn oorspronkelijke vraag hierboven is dat het “veilig” is om $_SERVER['HTTP_HOST']te gebruiken voor alle links op een site zonder zich zorgen te maken over XSS-aanvallen, zelfs wanneer gebruikt in formulieren.

Corrigeer me als ik het mis heb.


1, Autoriteit 100%

Dat is waarschijnlijk de eerste gedachte van iedereen. Maar het is een beetje moeilijker. Zie Chris Shiflett’s artikel SERVER_NAMEVersus HTTP_HOST.

Het lijkt erop dat er geen zilveren kogel is. Alleen wanneer u Apache forceren om de canonieke naam te gebruiken u altijd Download de juiste servernaam met SERVER_NAME.

Dus u gaat daarmee, of u controleert de hostnaam tegen een witte lijst:

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

2, Autoriteit 54%

Gewoon een extra noot – Als de server op een andere poort is, behalve 80 (zoals mogelijk is op een ontwikkelings- / intranetmachine) dan HTTP_HOSTbevat de poort, terwijl SERVER_NAMEniet.

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

(Tenminste, dat is wat ik heb opgemerkt in Prain Port-gebaseerde VirtualHosts)

Zoals Mike hieronder heeft opgemerkt, bevat HTTP_HOSTniet:443bij gebruik op HTTPS (tenzij je op een niet- standaardpoort, die ik niet heb getest).


Antwoord 3, autoriteit 20%

Gebruik een van beide. Ze zijn allebei even (on)veilig, aangezien SERVER_NAME in veel gevallen toch gewoon vanuit HTTP_HOST wordt ingevuld. Ik ga normaal gesproken voor HTTP_HOST, zodat de gebruiker op de exacte hostnaam blijft waarmee hij is begonnen. Als ik bijvoorbeeld dezelfde site op een .com- en .org-domein heb, wil ik niet iemand van .org naar .com sturen, vooral niet als ze inlogtokens op .org hebben die ze zouden verliezen als ze naar .com worden gestuurd. het andere domein.

Hoe dan ook, u hoeft er alleen maar zeker van te zijn dat uw webapp alleen reageert op domeinen waarvan u weet dat ze goed zijn. Dit kan worden gedaan (a) met een controle aan de applicatiezijde zoals die van Gumbo, of (b) door een virtuele host te gebruiken op de gewenste domeinnaam(en) die niet reageertop verzoeken die een onbekende host-header.

De reden hiervoor is dat als je toestaat dat je site onder een oude naam wordt geopend, je jezelf blootstelt aan DNS-rebinding-aanvallen (waarbij de hostnaam van een andere site naar je IP verwijst, een gebruiker toegang krijgt tot je site met de hostnaam van de aanvaller, vervolgens wordt de hostnaam verplaatst naar het IP-adres van de aanvaller, waarbij uw cookies/authenticatie worden meegenomen) en het kapen van de zoekmachine (waarbij een aanvaller zijn eigen hostnaam naar uw site verwijst en probeert de zoekmachines deze te laten zien als de ‘beste’ primaire hostnaam).

Blijkbaar gaat de discussie vooral over $_SERVER[‘PHP_SELF’] en waarom je het niet in het form action-attribuut zou moeten gebruiken zonder de juiste escapecodes om XSS-aanvallen te voorkomen.

Pff. Welnu, je zou ietsin elkattribuut moeten gebruiken zonder te escapen met htmlspecialchars($string, ENT_QUOTES), dus er is niets speciaals aan servervariabelen daar .


Antwoord 4, autoriteit 16%

Dit is een uitgebreide vertaling van wat Symfony gebruikt om de hostnaam te krijgen (zie het tweede voorbeeld voor een meer letterlijke vertaling):

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

Verouderd:

Dit is mijn vertaling naar kale PHP van een methode die wordt gebruikt in het Symfony-framework en die probeert de hostnaam op alle mogelijke manieren te krijgen in volgorde van best practice:

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

5, Autoriteit 2%

Ik ben niet zeker en vertrouw niet echt $_SERVER['HTTP_HOST']omdat het afhankelijk is van de kop van de klant. Op een andere manier, als een door de klant gevraagde domein niet van mij is, komen ze niet op mijn site omdat DNS- en TCP / IP-protocol het op de juiste bestemming wijzen. Ik weet het echter niet, indien mogelijk om de DNS, Network of Even Apache Server te kaarten. Om veilig te zijn, definieer ik hostnaam in de omgeving en vergelijk het met $_SERVER['HTTP_HOST'].

Toevoegen SetEnv MyHost domain.comin .htaccess-bestand op root en voeg de code toe in Common.php

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

Ik neem dit Common.php-bestand op in elke PHP-pagina. Deze pagina doet alles wat nodig is voor elk verzoek zoals session_start(), wijzig sessiecookie en weigeren als postmethode uit een ander domein komt.


6

XSSZAL ALTIJD aanwezig zijn, zelfs als u $_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']of $_SERVER['PHP_SELF']


7

Eerst wil ik u bedanken voor alle goede antwoorden en uitleg.
Dit is de methode die ik heb gemaakt op basis van al je antwoord om de basis-URL te krijgen. Ik gebruik het alleen in zeer zeldzame situaties. Er is dus geen grote focus op beveiligingsproblemen, zoals XSS-aanvallen. Misschien heeft iemand het nodig.

// Get base url
function getBaseUrl($array=false) {
    $protocol = "";
    $host = "";
    $port = "";
    $dir = "";  
    // Get protocol
    if(array_key_exists("HTTPS", $_SERVER) && $_SERVER["HTTPS"] != "") {
        if($_SERVER["HTTPS"] == "on") { $protocol = "https"; }
        else { $protocol = "http"; }
    } elseif(array_key_exists("REQUEST_SCHEME", $_SERVER) && $_SERVER["REQUEST_SCHEME"] != "") { $protocol = $_SERVER["REQUEST_SCHEME"]; }
    // Get host
    if(array_key_exists("HTTP_X_FORWARDED_HOST", $_SERVER) && $_SERVER["HTTP_X_FORWARDED_HOST"] != "") { $host = trim(end(explode(',', $_SERVER["HTTP_X_FORWARDED_HOST"]))); }
    elseif(array_key_exists("SERVER_NAME", $_SERVER) && $_SERVER["SERVER_NAME"] != "") { $host = $_SERVER["SERVER_NAME"]; }
    elseif(array_key_exists("HTTP_HOST", $_SERVER) && $_SERVER["HTTP_HOST"] != "") { $host = $_SERVER["HTTP_HOST"]; }
    elseif(array_key_exists("SERVER_ADDR", $_SERVER) && $_SERVER["SERVER_ADDR"] != "") { $host = $_SERVER["SERVER_ADDR"]; }
    //elseif(array_key_exists("SSL_TLS_SNI", $_SERVER) && $_SERVER["SSL_TLS_SNI"] != "") { $host = $_SERVER["SSL_TLS_SNI"]; }
    // Get port
    if(array_key_exists("SERVER_PORT", $_SERVER) && $_SERVER["SERVER_PORT"] != "") { $port = $_SERVER["SERVER_PORT"]; }
    elseif(stripos($host, ":") !== false) { $port = substr($host, (stripos($host, ":")+1)); }
    // Remove port from host
    $host = preg_replace("/:\d+$/", "", $host);
    // Get dir
    if(array_key_exists("SCRIPT_NAME", $_SERVER) && $_SERVER["SCRIPT_NAME"] != "") { $dir = $_SERVER["SCRIPT_NAME"]; }
    elseif(array_key_exists("PHP_SELF", $_SERVER) && $_SERVER["PHP_SELF"] != "") { $dir = $_SERVER["PHP_SELF"]; }
    elseif(array_key_exists("REQUEST_URI", $_SERVER) && $_SERVER["REQUEST_URI"] != "") { $dir = $_SERVER["REQUEST_URI"]; }
    // Shorten to main dir
    if(stripos($dir, "/") !== false) { $dir = substr($dir, 0, (strripos($dir, "/")+1)); }
    // Create return value
    if(!$array) {
        if($port == "80" || $port == "443" || $port == "") { $port = ""; }
        else { $port = ":".$port; } 
        return htmlspecialchars($protocol."://".$host.$port.$dir, ENT_QUOTES); 
    } else { return ["protocol" => $protocol, "host" => $host, "port" => $port, "dir" => $dir]; }
}

Other episodes