Apa perbedaan antara HTTP_HOST dan SERVER_NAME dalam PHP?

533

Apa perbedaan antara HTTP_HOSTdan SERVER_NAMEdalam PHP?

dimana:

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

Kapan Anda akan mempertimbangkan untuk menggunakan salah satunya dan mengapa?

Emanuil Rusev
sumber
14
"Saya biasanya menggunakan HTTP_HOST, sehingga pengguna tetap menggunakan nama host yang tepat yang mereka mulai. Misalnya jika saya memiliki situs yang sama pada domain .com dan .org, saya tidak ingin mengirim seseorang dari .org ke .com, khususnya jika mereka mungkin memiliki token masuk di .org yang akan hilang jika dikirim ke domain lain. " - Ini dan beberapa poin menarik lainnya dari stackoverflow.com/questions/1459739/...
Yarin
5
@Yarin, Jangan lupa untuk daftar putih-verifikasi hasilHTTP_HOST . Kalau tidak, seorang penyerang dapat memasukkan nilai apa pun dalam Host:permintaan HTTP dan membuat server menerimanya.
Pacerier
6
Pemula: Pertanyaan ini mengacu pada nilai-nilai yang biasanya diperoleh melalui $_SERVER['HTTP_HOST']atau$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Jawaban:

780

Ini HTTP_HOSTdiperoleh dari tajuk permintaan HTTP dan inilah yang sebenarnya digunakan klien sebagai "host target" dari permintaan. The SERVER_NAMEdidefinisikan dalam konfigurasi server. Yang mana untuk digunakan tergantung pada apa yang Anda butuhkan. Namun Anda sekarang harus menyadari bahwa yang satu adalah nilai yang dikendalikan klien yang mungkin tidak dapat diandalkan untuk digunakan dalam logika bisnis dan yang lainnya adalah nilai yang dikendalikan server yang lebih dapat diandalkan. Namun Anda perlu memastikan bahwa server web yang bersangkutan memiliki SERVER_NAMEkonfigurasi yang benar. Mengambil Apache HTTPD sebagai contoh, inilah kutipan dari dokumentasinya :

Jika tidak ServerNameditentukan, maka server mencoba untuk menyimpulkan nama host dengan melakukan pencarian terbalik pada alamat IP. Jika tidak ada port yang ditentukan dalam ServerName, maka server akan menggunakan port dari permintaan yang masuk. Untuk keandalan dan prediksi yang optimal, Anda harus menentukan nama host dan porta eksplisit menggunakan ServerNamearahan.


Pembaruan : setelah memeriksa jawaban Pekka pada pertanyaan Anda yang berisi tautan ke jawaban bobince bahwa PHP akan selalu mengembalikan HTTP_HOSTnilai SERVER_NAME, yang bertentangan dengan pengalaman PHP saya sendiri 4.x + Apache HTTPD 1.2.x dari beberapa tahun yang lalu , Saya meniup debu dari lingkungan XAMPP saya saat ini di Windows XP (Apache HTTPD 2.2.1 dengan PHP 5.2.8), memulainya, membuat halaman PHP yang mencetak kedua nilai, membuat aplikasi uji Java menggunakan URLConnectionuntuk memodifikasi Hostheader dan tes mengajari saya bahwa ini memang (salah).

Setelah pertama kali mencurigai PHP dan menggali dalam beberapa laporan bug PHP tentang subjek, saya mengetahui bahwa akar masalahnya ada di server web yang digunakan, bahwa itu salah mengembalikan HTTP Hostheader ketika SERVER_NAMEdiminta. Jadi saya menggali laporan bug HTTPD Apache menggunakan berbagai kata kunci mengenai subjek dan akhirnya saya menemukan bug terkait . Perilaku ini diperkenalkan sejak sekitar Apache HTTPD 1.3. Anda perlu set UseCanonicalNamedirektif untuk ondi <VirtualHost>masuknya ServerNamedi httpd.conf(juga memeriksa peringatan di bagian bawah dokumen !).

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

Ini berhasil untuk saya.

Ringkasnya, SERVER_NAMElebih dapat diandalkan, tetapi Anda bergantung pada konfigurasi server!

BalusC
sumber
5
Oke, ini menyelesaikan masalah saya, yang tidak terkait dengan OP tetapi relevan. Saya sangat khawatir tentang masalah keamanan dengan menggunakan apa pun yang bisa disediakan oleh peramban. Jawaban ini sangat membantu. Terima kasih telah meluangkan waktu untuk menyatukannya.
Yitzhak
2
Mengapa menurut Anda HTTP_HOST tidak dapat diandalkan? Ya itu disediakan oleh pengguna, tetapi jika pengguna memberikan nilai palsu, konfigurasi server Anda akan secara otomatis mengembalikan 503 dan skrip PHP Anda bahkan tidak akan dijalankan!
Pacerier
1
@Pacerier: pada saat menulis jawaban ini, ternyata tidak. Versi disebutkan dalam jawabannya. Saya tidak mengikuti PHP lagi, jadi saya tidak bisa mengatakan apakah itu memang berubah dalam versi yang lebih baru.
BalusC
2
Cara mudah untuk menipu Apache dari WinXP adalah dengan menambahkan baris ke file 'host' yang menyatakan bahwa IP server ditugaskan ke domain lain, seperti ini: "127.0.0.1 mydomain.com". Saya sering menggunakan ini untuk menunjukkan situs web lokal menipu pemirsa saya untuk berpikir saya punya koneksi internet dan situs dimuat dengan sangat cepat. Anda bisa pergi ke arah lain, dan menipu Apache untuk menganggapnya berjalan secara lokal, dengan "173.194.41.5 localhost", jadi Anda seharusnya tidak pernah mempercayai SERVER_NAME sepenuhnya kecuali Anda yakin bahwa Apache Anda dikonfigurasi dengan baik.
vicenteherrera
1
Saya hanya ingin menambahkan, bahwa NGINX + PHP-FPM mengembalikan nilai yang ditetapkan oleh server_namearahan. Apalagi jika tidak server_namediatur juga _SERVER["SERVER_NAME"]akan kosong.
white_gecko
69

HTTP_HOSTadalah host target yang dikirim oleh klien. Itu dapat dimanipulasi secara bebas oleh pengguna. Tidak masalah untuk mengirim permintaan ke situs Anda meminta HTTP_HOSTnilai www.stackoverflow.com.

SERVER_NAMEberasal dari VirtualHostdefinisi server dan karenanya dianggap lebih dapat diandalkan. Namun, itu juga dapat dimanipulasi dari luar dalam kondisi tertentu yang terkait dengan bagaimana server web Anda diatur: Lihat ini Pertanyaan SO ini yang berkaitan dengan aspek keamanan dari kedua variasi.

Anda tidak harus bergantung pada keduanya untuk aman. Yang mengatakan, apa yang harus digunakan sangat tergantung pada apa yang ingin Anda lakukan. Jika Anda ingin menentukan domain yang digunakan skrip Anda, Anda dapat menggunakan dengan aman HTTP_HOSTselama nilai tidak valid yang berasal dari pengguna jahat tidak dapat merusak apa pun.

Pekka
sumber
8
Ya tetapi permintaan yang meminta nilai HTTP_HOST dari www.stackoverflow.com akan ditolak oleh sebagian besar server HTTP di muka sehingga skrip PHP bahkan tidak akan melihat permintaan!
Pacerier
2
@Pacerier benar, tetapi tidak selalu jika server tidak dikonfigurasi dengan benar.
Pekka
1
Seperti yang disebutkan dalam posting BalusC, ketika Anda mengakses virtualhost Apache dengan IP, kedua variabel ini berisi IP (secara default), bukan nama server yang sebenarnya. Anda harus menggunakan UseCanonicalName ondi httpd.conf untuk memaksa SERVER_NAMEmenjadi nama server yang sebenarnya.
Simon East
@ Pekka 웃, Jika server tidak dikonfigurasi dengan benar, $_SERVER['SERVER_NAME']tidak akan berfungsi juga . Server yang dikonfigurasi dengan buruk akan ditetapkan $_SERVER['SERVER_NAME']berdasarkan nilai Host:permintaan klien . Keduanya sama.
Pacerier
Jawaban yang bagus, tetapi saya tidak akan menganggap hosting virtual.
Anthony Rutledge
55

Seperti yang saya sebutkan dalam jawaban ini , jika server berjalan pada port selain 80 (seperti yang umum pada mesin pengembangan / intranet) maka HTTP_HOSTberisi port, sementara SERVER_NAMEtidak.

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

(Setidaknya itulah yang saya perhatikan di virtualhosts berbasis port Apache)

Perhatikan bahwa HTTP_HOSTtidak tidak mengandung :443ketika berjalan di HTTPS (kecuali jika Anda berjalan pada port non-standar, yang saya belum diuji).

Seperti yang telah dicatat orang lain, keduanya juga berbeda ketika menggunakan IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Simon Timur
sumber
2
Kapan mereka akan memperbaiki perilaku berbahaya ini?
Pacerier
27

Harap dicatat bahwa jika Anda ingin menggunakan IPv6, Anda mungkin ingin menggunakan HTTP_HOSTdaripada SERVER_NAME. Jika Anda memasukkan http://[::1]/variabel lingkungan akan menjadi sebagai berikut:

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

Ini berarti, bahwa jika Anda melakukan mod_rewrite misalnya, Anda mungkin mendapatkan hasil yang buruk. Contoh untuk pengalihan 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}/

Ini HANYA berlaku jika Anda mengakses server tanpa nama host.

Daniel Marschall
sumber
1
SiteGround, di http in-house mereka ke https kode redirect, gunakanhttps://%{SERVER_NAME}%{REQUEST_URI}
IXN
6

Jika Anda ingin memeriksa melalui server.php atau apa pun, Anda ingin menyebutnya dengan yang berikut:

<?php
    phpinfo(INFO_VARIABLES);
?>

atau

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

    print_r($_SERVER);
?>

Kemudian akses dengan semua URL yang valid untuk situs Anda dan periksa perbedaannya.

stevewh
sumber
5

Tergantung apa yang ingin saya ketahui. SERVER_NAME adalah nama host dari server, sementara HTTP_HOST adalah host virtual yang terhubung dengan klien.

Rowland Shaw
sumber
4
Rowland tidak sepenuhnya benar, SERVER_NAMEbiasanya nama VirtualHost, bukan server itu sendiri. Dan di Apache, SERVER_NAMEsering diisi dengan nilai yang sama dengan HTTP_HOST(lihat jawaban BalusC).
Simon East
1
@Simon, Karena sebagian besar host sekarang VirtualHost, apa yang Anda maksud dengan nama "server itu sendiri"?
Pacerier
Jika Anda menjalankan server pribadi virtual (VPS) dengan satu situs web, Anda tidak perlu berasumsi bahwa itu SERVER_NAMEberlaku untuk host virtual. Namun, seseorang masih dapat menggunakan pengaturan host virtual untuk satu situs. Banyak orang menggunakan hosting bersama, jadi saya mengerti maksud Anda.
Anthony Rutledge
2

Perlu beberapa saat bagi saya untuk memahami apa yang dimaksud orang dengan ' SERVER_NAMElebih dapat diandalkan'. Saya menggunakan server bersama dan tidak memiliki akses ke arahan host virtual. Jadi, saya menggunakan mod_rewrite .htaccess untuk memetakan yang berbeda HTTP_HOSTke direktori yang berbeda. Dalam hal ini, itu HTTP_HOSTyang bermakna.

Situasi serupa jika seseorang menggunakan host virtual berbasis nama: ServerNamearahan dalam host virtual hanya mengatakan nama host yang akan dipetakan ke host virtual ini. Intinya adalah bahwa, dalam kedua kasus, nama host yang disediakan oleh klien selama permintaan ( HTTP_HOST), harus dicocokkan dengan nama di dalam server, yang dengan sendirinya dipetakan ke direktori. Apakah pemetaan dilakukan dengan arahan host virtual atau dengan aturan mod_rewrite htaccess adalah sekunder di sini. Dalam kasus ini, HTTP_HOSTakan sama dengan SERVER_NAME. Saya senang bahwa Apache dikonfigurasi dengan cara itu.

Namun, situasinya berbeda dengan host virtual berbasis IP. Dalam hal ini dan hanya dalam kasus ini, SERVER_NAMEdan HTTP_HOSTdapat berbeda, karena sekarang klien memilih server dengan IP, bukan dengan namanya. Memang, mungkin ada konfigurasi khusus di mana ini penting.

Jadi, mulai dari sekarang, saya akan menggunakan SERVER_NAME, kalau-kalau kode saya porting dalam konfigurasi khusus ini.

Dominic108
sumber
2

Dengan asumsi seseorang memiliki pengaturan yang sederhana (CentOS 7, Apache 2.4.x, dan PHP 5.6.20) dan hanya satu situs web (tidak menggunakan hosting virtual) ...

Dalam pengertian PHP, $_SERVER['SERVER_NAME']adalah elemen register PHP di $_SERVERsuperglobal berdasarkan pada konfigurasi Apache Anda ( **ServerName**direktif dengan UseCanonicalName On) di httpd.conf (baik itu dari file konfigurasi host virtual yang disertakan, apa pun, dll ...). HTTP_HOST diturunkan dari hostheader HTTP . Perlakukan ini sebagai input pengguna. Saring dan validasi sebelum menggunakan.

Berikut adalah contoh tempat saya menggunakan $_SERVER['SERVER_NAME']sebagai dasar untuk perbandingan. Metode berikut ini dari kelas anak beton yang saya buat bernama ServerValidator(child of Validator). ServerValidatormemeriksa enam atau tujuh elemen dalam $ _SERVER sebelum menggunakannya.

Dalam menentukan apakah permintaan HTTP adalah POST, saya menggunakan metode ini.

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

Pada saat metode ini dipanggil, semua pemfilteran dan validasi elemen $ _SERVER yang relevan akan terjadi (dan set properti yang relevan).

Garis ...

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

... memeriksa apakah $_SERVER['HTTP_HOST']nilainya (akhirnya berasal dari hosttajuk HTTP yang diminta ) cocok $_SERVER['SERVER_NAME'].

Sekarang, saya menggunakan berbicara superglobal untuk menjelaskan contoh saya, tapi itu hanya karena beberapa orang tidak terbiasa dengan INPUT_GET, INPUT_POST, dan INPUT_SERVERdalam hal filter_input_array().

Intinya adalah, saya tidak menangani permintaan POST pada server saya kecuali semua empat kondisi terpenuhi. Oleh karena itu, dalam hal permintaan POST, kegagalan untuk menyediakan hostheader HTTP (keberadaan diuji sebelumnya) mantra malapetaka untuk browser HTTP 1.0 yang ketat . Selain itu, tuan rumah yang diminta harus sesuai nilai untuk ServerNamedi httpd.conf , dan, dengan extention, nilai untuk $_SERVER('SERVER_NAME')di $_SERVERsuperglobal. Sekali lagi, saya akan menggunakan INPUT_SERVERdengan fungsi filter PHP, tetapi Anda tahu maksud saya.

Perlu diingat bahwa Apache sering menggunakan ServerNamedi pengalihan standar (seperti meninggalkan garis miring off URL: Contoh, http://www.foo.com menjadi http://www.foo.com/ ), bahkan jika Anda tidak menggunakan penulisan ulang URL.

Saya menggunakan $_SERVER['SERVER_NAME']sebagai standar, bukan $_SERVER['HTTP_HOST']. Ada banyak bolak-balik tentang masalah ini. $_SERVER['HTTP_HOST']mungkin kosong, jadi ini seharusnya tidak menjadi dasar untuk membuat konvensi kode seperti metode publik saya di atas. Tapi, hanya karena keduanya dapat diatur tidak menjamin mereka akan sama. Pengujian adalah cara terbaik untuk mengetahui dengan pasti (mengingat versi Apache dan versi PHP).

Anthony Rutledge
sumber
0

Seperti yang dikatakan balusC SERVER_NAME tidak dapat diandalkan dan dapat diubah di konfigurasi apache, konfigurasi nama server dari server dan firewall yang dapat berada di antara Anda dan server.

Fungsi berikut selalu mengembalikan host nyata (host yang diketik pengguna) tanpa port dan ini hampir dapat diandalkan:

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

$ _SERVER ['SERVER_NAME'] didasarkan pada konfigurasi server web Anda. $ _SERVER ['HTTP_HOST'] didasarkan pada permintaan dari klien.

Vitalie
sumber