php - query_string - request_uri




PHP $_SERVER['HTTP_HOST']與$_SERVER['SERVER_NAME'],我是否正確理解手冊頁? (6)

我做了很多搜索,並且還閱讀了PHP $_SERVER文檔。 我是否有權利使用我的PHP腳本來使用我的網站中使用的簡單鏈接定義?

$_SERVER['SERVER_NAME']基於你的Web服務器配置文件(在我的情況下是Apache2),並且取決於幾個指令:(1)VirtualHost,(2)ServerName,(3)UseCanonicalName等

$_SERVER['HTTP_HOST']基於來自客戶端的請求。

因此,在我看來,為了使我的腳本盡可能兼容,使用正確的腳本將是$_SERVER['HTTP_HOST'] 。 這個假設是否正確?

後續評論:

我想我讀了這篇文章後有點偏執,並指出有些人說:“他們不會相信任何$_SERVER變數:”

顯然這個討論主要是關於$_SERVER['PHP_SELF']以及為什麼你不應該在form action屬性中使用它而沒有正確的轉義以防止XSS攻擊。

關於我上面的原始問題的結論是,對於站點上的所有鏈接使用$_SERVER['HTTP_HOST']而不必擔心XSS攻擊是“安全的”,即使在表單中使用時也是如此。

如果我錯了,請糾正我。


對於站點上的所有鏈接使用$_SERVER['HTTP_HOST']而不必擔心XSS攻擊(即使在表單中使用)是否“安全”?

是的,使用$_SERVER['HTTP_HOST'] (甚至$_GET$_POST是安全的,只要您在接受它們之前驗證它們即可。 這是我為安全生產服務器所做的:

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

$_SERVER['HTTP_HOST']的優點在於它的行為比$_SERVER['SERVER_NAME']更明確。 對比度➫➫

當前請求中的Host:頭部的內容(如果有)。

有:

當前腳本執行的服務器主機的名稱。

使用更好的定義接口,如$_SERVER['HTTP_HOST']意味著更多的SAPI將使用可靠的明確定義的行為來實現它。 (與其他不同)但是,它仍然完全依賴於➫➫

不能保證每個Web服務器都會提供這些[ $_SERVER條目]中的任何一個; 服務器可能會省略一些,或者提供這裡沒有列出的其他人。

要理解如何正確檢索主機名,首先您需要了解的是,僅包含代碼的服務器無法知道(在網絡上驗證其自己的名稱的前提條件)。 它需要與提供自己名稱的組件接口。 這可以通過以下方式完成:

  • 本地配置文件

  • 本地數據庫

  • 硬編碼的源代碼

  • 外部請求( curl

  • 客戶端/攻擊者的Host:請求

  • 等等

通常通過本地(SAPI)配置文件完成。 請注意,您已正確配置它,例如在Apache➫➫中

為了使動態虛擬主機看起來像一個普通虛擬主機,需要做一些偽裝的事情。

最重要的是Apache用於生成自引用URL的服務器名稱等。它使用ServerName指令進行配置,並且可通過SERVER_NAME環境變量向CGI提供。

運行時使用的實際值 UseCanonicalName設置控制

使用 UseCanonicalName Off服務器名稱來自請求中Host:標頭的內容。 使用 UseCanonicalName DNS它來自虛擬主機IP地址的反向DNS查找。 前者設置用於基於名稱的動態虛擬主機,後者用於基於IP的主機。

如果 Apache無法找出服務器名稱,因為沒有Host:標頭或DNS查找失敗, 使用ServerName配置的值代替。


使用任一。 它們同樣安全,因為在很多情況下SERVER_NAME只是從HTTP_HOST填充。 我通常會使用HTTP_HOST,以便用戶保持開始時的確切主機名。 例如,如果我在.com和.org域中擁有相同的網站,我不希望將某人從.org發送到.com,特別是如果他們可能在.org上擁有登錄令牌,並且發送給另一個領域。

無論哪種方式,你只需要確定你的web應用程序將只響應已知好的域名。 這可以通過(a)通過像Gumbo這樣的應用程序端檢查來完成,或者(b)通過在你想要的域名上使用虛擬主機, 它不響應給出未知主機頭的請求。

原因是如果你允許你的站點被任何舊名稱訪問,你就會面對DNS重新綁定攻擊(其他站點的主機名指向你的IP,用戶使用攻擊者的主機名訪問你的站點,然後主機名被移動到攻擊者的IP地址,帶著你的cookies / auth)和搜索引擎劫持(攻擊者在你的站點指向他們自己的主機名,並試圖讓搜索引擎將其視為'最好'的主要主機名)。

顯然這個討論主要是關於$ _SERVER ['PHP_SELF']以及為什麼你不應該在form action屬性中使用它而沒有正確的轉義以防止XSS攻擊。

Pfft。 那麼你不應該在沒有使用htmlspecialchars($string, ENT_QUOTES)轉義的情況下在任何屬性中使用任何屬性,所以在那裡沒有什麼特別的服務器變量。


即使使用$_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME']$_SERVER['PHP_SELF'] XSS也將始終存在


另外需要注意的是,如果服務器運行在80以外的端口上(如開發/內聯網機器上常見的那樣),那麼HTTP_HOST包含端口,而SERVER_NAME則不包含端口。

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

(至少這是我在Apache基於端口的虛擬主機中註意到的)

正如Mike在下面指出的那樣,在HTTPS上運行時, HTTP_HOST不包含:443 (除非您運行的是非標準端口,而我沒有測試過)。


這可能是每個人的第一個想法。 但這有點困難。 請參閱Chris Shiflett的文章SERVER_NAMEHTTP_HOST

似乎沒有銀彈。 只有當您強制Apache使用規範名稱時,您才會始終使用SERVER_NAME獲取正確的服務器名稱。

所以你要么去那個,要么檢查白名單中的主機名:

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

這是Symfony用於獲取主機名的詳細翻譯( 參見第二個示例以獲得更直接的翻譯 ):

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

已過期:

這是我翻譯為在PHP中使用Symfony框架中使用的一種方法,它嘗試按照最佳實踐的順序從各種方式獲取主機名:

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






owasp