Cara mengetahui apakah Anda menggunakan HTTPS tanpa $ _SERVER ['HTTPS']

190

Saya telah melihat banyak tutorial online yang mengatakan bahwa Anda perlu memeriksa $_SERVER['HTTPS']apakah server terhubung dengan HTTPS. Masalah saya adalah bahwa pada beberapa server yang saya gunakan, $_SERVER['HTTPS']adalah variabel yang tidak ditentukan yang menghasilkan kesalahan. Apakah ada variabel lain yang dapat saya periksa yang harus selalu didefinisikan?

Untuk lebih jelasnya, saya saat ini menggunakan kode ini untuk menyelesaikan jika itu adalah koneksi HTTPS:

if(isset($_SERVER['HTTPS'])) {
    if ($_SERVER['HTTPS'] == "on") {
        $secure_connection = true;
    }
}
Tyler Carter
sumber
Bagaimanapun, server-server di mana $ _SERVER ['HTTPS'] tidak ditentukan sedang berjalan pada HTTPS?
Freddy
Sebenarnya, salah satunya adalah server WAMP rumah saya. Dan saya tidak percaya itu berjalan di HTTPS.
Tyler Carter
@TylerCarter, Metode alternatif adalah menggunakan Securecookie. Hati-hati dengan gotcha.
Pacerier

Jawaban:

280

Ini harus selalu berfungsi bahkan ketika $_SERVER['HTTPS']tidak ditentukan:

function isSecure() {
  return
    (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
    || $_SERVER['SERVER_PORT'] == 443;
}

Kode ini kompatibel dengan IIS.

Dari dokumentasi PHP.net dan komentar pengguna :

1) Setel ke nilai non-kosong jika skrip ditanyai melalui protokol HTTPS.

2) Perhatikan bahwa saat menggunakan ISAPI dengan IIS, nilainya akan "mati" jika permintaan tidak dilakukan melalui protokol HTTPS. (Perilaku yang sama telah dilaporkan untuk IIS7 yang menjalankan PHP sebagai aplikasi Fast-CGI).

Juga, server Apache 1.x (dan instalasi yang rusak) mungkin tidak $_SERVER['HTTPS']ditentukan bahkan jika tersambung dengan aman. Meskipun tidak dijamin, koneksi pada port 443, oleh konvensi , kemungkinan menggunakan soket aman , maka port memeriksa tambahan.

Catatan tambahan: jika ada penyeimbang beban antara klien dan server Anda, kode ini tidak menguji koneksi antara klien dan penyeimbang beban, tetapi koneksi antara penyeimbang beban dan server Anda. Untuk menguji koneksi sebelumnya, Anda harus menguji menggunakan HTTP_X_FORWARDED_PROTOheader, tetapi jauh lebih rumit untuk dilakukan; lihat komentar terbaru di bawah jawaban ini.

Gras Double
sumber
50
Nb: port 443 tidak menjamin koneksi dienkripsi
ErichBSchulz
2
@ DavidRodrigues Itu tidak benar. Anda dapat menggunakan HTTP / HTTPS melalui port apa pun yang Anda inginkan. getservbyname()hanya referensi, bukan kenyataan, dan tidak dengan cara apa pun menjamin bahwa HTTPS berjalan di port 443.
Brad
1
Saya punya masalah kecil dengan $_SERVER['SERVER_PORT'] !== 443saya harus melemparkan $_SERVER['SERVER_PORT]ke integer seperti:intval($_SERVER['SERVER_PORT]) !== 443
meconroy
1
1) Pemeriksaan port server adalah tambahan untuk server sheetty, paling baik untuk menghapusnya jika tidak diperlukan. 2) Perhatikan ini adalah perbandingan longgar dalam jawaban saya;)
Gras Double
1
Satu lagi masalah kecil yang saya temui hari ini. Server mengembalikan 'OFF' bukan 'off' - strtolower($_SERVER['HTTPS']) !== 'off'lakukan triknya.
jhummel
117

Solusi saya (karena kondisi standar [$ _SERVER ['HTTPS'] == 'on'] tidak berfungsi pada server di belakang load balancer) adalah:

$isSecure = false;
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
    $isSecure = true;
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') {
    $isSecure = true;
}
$REQUEST_PROTOCOL = $isSecure ? 'https' : 'http';

HTTP_X_FORWARDED_PROTO: standar de facto untuk mengidentifikasi protokol asal permintaan HTTP, karena proxy terbalik (load balancer) dapat berkomunikasi dengan server web menggunakan HTTP bahkan jika permintaan ke proxy terbalik adalah HTTPS http: //en.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers

temuraru
sumber
4
Ini adalah solusinya jika Anda menggunakan proksi balik pernis.
Reto Zahner
1
Masalah saya teratasi dengan solusi ini. (PHP - AWS Elastic beanstalk)
user4826347
Ini adalah solusinya jika Anda menggunakan load balancers.
Abhishek Saini
Apa jenis file yang Anda tempelkan ini? Saya berasumsi ini tidak ada dalam file .htaccess?
Jordan
5
Ini juga berfungsi untuk HTTPS gratis yang disediakan oleh CloudFlare.
AnthonyVO
82

Chacha, per dokumentasi PHP: "Setel ke nilai yang tidak kosong jika skrip ditanyai melalui protokol HTTPS." Jadi pernyataan if Anda di sana akan mengembalikan false dalam banyak kasus di mana HTTPS memang aktif. Anda ingin memverifikasi yang $_SERVER['HTTPS']ada dan tidak kosong. Dalam kasus di mana HTTPS tidak diatur dengan benar untuk server yang diberikan, Anda dapat mencoba memeriksa apakah $_SERVER['SERVER_PORT'] == 443.

Tetapi perhatikan bahwa beberapa server juga akan diatur $_SERVER['HTTPS']ke nilai yang tidak kosong, jadi pastikan untuk memeriksa variabel ini juga.

Referensi: Dokumentasi untuk $_SERVERdan $HTTP_SERVER_VARS[usang]

hobodave
sumber
12
gunakan $ _SERVER ['SERVER_PORT'] bisa rumit ... misalnya ispconfig menggunakan port 81 sebagai port aman, jadi katakanlah 443 adalah port "default" untuk ssl.
Gabriel Sosa
@Gabriel Sosa - Benar, tetapi peringatan dapat diatasi berdasarkan kasus per kasus. Jawaban @ hobodave akan bekerja paling baik.
Tim Post
Perhatikan bahwa ini tidak akan bekerja di belakang proxy terbalik. Seseorang mungkin mempertimbangkan untuk memeriksa HTTP_X_FORWARDED_PROTOatau HTTP_X_FORWARDED_SSLjuga.
paolo
1
Saya setuju bahwa pilihan terakhir adalah nomor port, jadi inilah cek saya: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) yang tidak termasuk cek port sama sekali. Jangan ragu untuk menambahkan. :-)
Roland
@ Paolo di belakang proxy terbalik SetEnvIf X-Forwarded-SSL on HTTPS=onakan melakukan trik. Tetapi ini tidak akan berhasil REQUEST_SCHEMEkarena php tampaknya lebih baik digunakan$_SERVER['HTTPS']
Antony Gibbs
14

Ini juga berfungsi ketika $_SERVER['HTTPS']tidak ditentukan

if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
    //enable secure connection
}
Thamaraiselvam
sumber
2
Mereka adalah beberapa server $_SERVER['HTTPS']yang belum ditentukan https diaktifkan. Bagaimana tentang itu ?
John Max
1
@JohnMax SERVER_PORTdidefinisikan selalu yang memecahkan masalah yang tidak ditentukanHTTPS
Thamaraiselvam
11

Saya baru saja mengalami masalah ketika saya menjalankan server menggunakan Apache mod_ssl, namun sebuah phpinfo () dan var_dump ($ _SERVER) menunjukkan bahwa PHP masih berpikir saya berada di port 80.

Ini solusi saya untuk siapa saja dengan masalah yang sama ....

<VirtualHost *:443>
  SetEnv HTTPS on
  DocumentRoot /var/www/vhost/scratch/content
  ServerName scratch.example.com
</VirtualHost>

Baris yang perlu diperhatikan adalah garis SetEnv. Dengan ini di tempat dan setelah restart, Anda harus memiliki variabel lingkungan HTTPS yang selalu Anda impikan

thomas-peter
sumber
5
Lebih baik pastikan HTTPS benar-benar berfungsi; itu akan membuat server berbohong kepada Anda jika tidak.
Brad Koch
Anda juga memerlukan modul SetEnv agar ini berfungsi. Ini diaktifkan secara default, tetapi Anda tidak pernah tahu apa yang mungkin dinonaktifkan oleh admin server.
toon81
Sangat berguna jika Anda berada di buruh pelabuhan melalui proxy terbalik. Terima kasih!
dikirill
8

Membuat fungsi saya sendiri dari membaca semua posting sebelumnya:

public static function isHttps()
{
    if (array_key_exists("HTTPS", $_SERVER) && 'on' === $_SERVER["HTTPS"]) {
        return true;
    }
    if (array_key_exists("SERVER_PORT", $_SERVER) && 443 === (int)$_SERVER["SERVER_PORT"]) {
        return true;
    }
    if (array_key_exists("HTTP_X_FORWARDED_SSL", $_SERVER) && 'on' === $_SERVER["HTTP_X_FORWARDED_SSL"]) {
        return true;
    }
    if (array_key_exists("HTTP_X_FORWARDED_PROTO", $_SERVER) && 'https' === $_SERVER["HTTP_X_FORWARDED_PROTO"]) {
        return true;
    }
    return false;
}
ling
sumber
7

Jika Anda menggunakan Apache, Anda selalu dapat mengandalkan

$_SERVER["REQUEST_SCHEME"]

untuk memverifikasi skema URL yang diminta. Tetapi, seperti disebutkan dalam jawaban lain, lebih baik memverifikasi parameter lain sebelum mengasumsikan SSL benar-benar digunakan.

Ed de Almeida
sumber
ini berfungsi pada XAMPP tetapi tidak pada centos / apache2 + PHP ... jadi tidak bisa dipercaya.
Firas Abd Alrahman
5

Jawaban NYATA: siap untuk salin-tempel ke skrip [konfigurasi]

/* configuration settings; X=edit may 10th '11 */
$pv_sslport=443; /* for it might be different, as also Gabriel Sosa stated */
$pv_serverport=80; /* X */
$pv_servername="mysite.com"; /* X */

/* X appended after correction by Michael Kopinsky */
if(!isset($_SERVER["SERVER_NAME"]) || !$_SERVER["SERVER_NAME"]) {
    if(!isset($_ENV["SERVER_NAME"])) {
        getenv("SERVER_NAME");
        // Set to env server_name
        $_SERVER["SERVER_NAME"]=$_ENV["SERVER_NAME"];
    }
}
if(!$_SERVER["SERVER_NAME"]) (
    /* X server name still empty? ... you might set $_SERVER["SERVER_NAME"]=$pv_servername; */
}

if(!isset($_SERVER["SERVER_PORT"]) || !$_SERVER["SERVER_PORT"]) {
    if(!isset($_ENV["SERVER_PORT"])) {
        getenv("SERVER_PORT");
        $_SERVER["SERVER_PORT"]=$_ENV["SERVER_PORT"];
    }
}
if(!$_SERVER["SERVER_PORT"]) (
    /* X server port still empty? ... you might set $_SERVER["SERVER_PORT"]=$pv_serverport; */
}

$pv_URIprotocol = isset($_SERVER["HTTPS"]) ? (($_SERVER["HTTPS"]==="on" || $_SERVER["HTTPS"]===1 || $_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://") :  (($_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://");

$pv_URIprotocolsekarang benar dan siap digunakan; contoh $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Secara alami, string dapat diganti dengan TRUE dan FALSE juga. PV adalah singkatan dari PortalPress Variable karena merupakan copy-paste langsung yang akan selalu berfungsi. Bagian ini dapat digunakan dalam skrip produksi.

Igor M. - PortalPress.org
sumber
3

Saya tidak berpikir bahwa menambahkan port adalah ide yang bagus - khususnya ketika Anda mendapatkan banyak server dengan build yang berbeda. itu hanya menambah satu hal lagi yang perlu diingat untuk berubah. melihat doc saya pikir baris terakhir kaiser cukup bagus, sehingga:

if(!empty($_SERVER["HTTPS"]))
  if($_SERVER["HTTPS"]!=="off")
    return 1; //https
  else
    return 0; //http
else
  return 0; //http

sepertinya cukup sempurna.

sp3c1
sumber
3

Satu-satunya metode yang dapat diandalkan adalah yang dijelaskan oleh Igor M.

$pv_URIprotocol = isset($_SERVER["HTTPS"]) ? (($_SERVER["HTTPS"]==="on" || $_SERVER["HTTPS"]===1 || $_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://") :  (($_SERVER["SERVER_PORT"]===$pv_sslport) ? "https://" : "http://");

Pertimbangkan yang berikut: Anda menggunakan nginx dengan fastcgi, secara default (debian, ubuntu) fastgi_params mengandung arahan:

fastcgi_param HTTPS $ https;

jika Anda TIDAK menggunakan SSL, itu akan diterjemahkan sebagai nilai kosong, bukan 'tidak aktif', bukan 0 dan Anda akan dikutuk.

http://unpec.blogspot.cz/2013/01/nette-nginx-php-fpm-redirect.html

Galvani
sumber
3

Saya menemukan params ini dapat diterima juga dan lebih dari itu kemungkinan tidak memiliki positif palsu ketika berpindah server web.

  1. $ _SERVER ['HTTPS_KEYSIZE']
  2. $ _SERVER ['HTTPS_SECRETKEYSIZE']
  3. $ _SERVER ['HTTPS_SERVER_ISSUER']
  4. $ _SERVER ['HTTPS_SERVER_SUBJECT']

    if($_SERVER['HTTPS_KEYSIZE'] != NULL){/*do foobar*/}
Werezywolf
sumber
Ini tidak memberi tahu Anda apa pun tentang penggunaan HTTPS dengan load balancer / proxy.
Brad
3

Cara terpendek yang saya gunakan:

$secure_connection = !empty($_SERVER['HTTPS']);

Jika jika https digunakan, maka $ secure_connection benar.

Markus Zeller
sumber
echo (!empty($_SERVER['HTTPS'])?'https':'http');memberi Anda httpatauhttps
Xavi Esteve
, buatlah(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
2

Anda dapat memeriksa $_SERVER['SERVER_PORT']karena SSL biasanya berjalan pada port 443, tetapi ini tidak mudah.

pix0r
sumber
$ _SERVER ['SERVER_PORT'] melakukannya.
Tyler Carter
2

Apa pendapatmu tentang ini?

if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
    $scheme = 'https';
else
    $scheme = 'http';
toni rmc
sumber
Ya ada. Jika Anda hanya mengandalkan kosong () PHP akan keluar dengan kesalahan jika tidak ada indeks 'HTTPS'.
toni rmc
3
"empty () pada dasarnya setara dengan! isset ($ var) || $ var == false" - php.net/manual/en/function.empty.php
John Magnolia
2
Kamu benar. Lucu aku merindukan yang itu. Saya selalu berpikir kosong () akan gagal jika variabel tidak ada.
toni rmc
2

Di server saya (Ubuntu 14.10, Apache 2.4, php 5.5) variabel $_SERVER['HTTPS']tidak disetel saat skrip php dimuat melalui https. Saya tidak tahu apa yang salah. Tetapi baris berikut dalam .htaccessfile memperbaiki masalah ini:

RewriteEngine on

RewriteCond %{HTTPS} =on [NC] 
RewriteRule .* - [E=HTTPS:on,NE]
Karry
sumber
1

Berikut adalah fungsi yang dapat digunakan kembali yang telah saya gunakan untuk sementara waktu. HTH.

Catatan: Nilai HTTPS_PORT (yang merupakan konstanta ubahsuaian dalam kode saya) dapat bervariasi pada lingkungan Anda, misalnya mungkin 443 atau 81.

/**
 * Determine if this is a secure HTTPS connection
 * 
 * @return  bool    True if it is a secure HTTPS connection, otherwise false.
 */
function isSSL()
{
    if (isset($_SERVER['HTTPS'])) {
        if ($_SERVER['HTTPS'] == 1) {
            return true;
        } elseif ($_SERVER['HTTPS'] == 'on') {
            return true;
        }
    } elseif ($_SERVER['SERVER_PORT'] == HTTPS_PORT) {
        return true;
    }

    return false;
}
Crmpicco
sumber
1

hanya untuk menarik, krom kenari saat ini mengirimkan

HTTPS : 1

ke server, dan tergantung pada bagaimana server dikonfigurasi dapat berarti Anda mendapatkan kembali yang berikut

HTTPS : 1, on

Ini merusak aplikasi kami karena kami menguji jika ada, yang jelas tidak. Saat ini, hanya kenari krom yang melakukan hal ini, tetapi perlu dicatat bahwa hal-hal dari kenari umumnya mendarat di krom "normal" beberapa saat kemudian.

John Smith
sumber
1

Jika Anda menggunakan nginx sebagai sistem loadbalancing, periksa $ _SERVER ['HTTP_HTTPS'] == 1 cek lain akan gagal untuk ssl.

dasar
sumber
1
$secure_connection = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || (!empty($_SERVER['HTTP_HTTPS']) && $_SERVER['HTTP_HTTPS'] != 'off') || $_SERVER['REQUEST_SCHEME'] == 'https' || $_SERVER['SERVER_PORT'] == 443) ? true : false;

Kode memeriksa segala kemungkinan dan berfungsi juga di server web IIS. Chrome sejak v44 tidak menetapkan HTTP header: 1 jadi memeriksa HTTP_HTTPS tidak masalah. Jika kode ini tidak cocok dengan https, itu berarti server web atau server proxy Anda tidak terkonfigurasi dengan baik. Apache sendiri mengatur flag HTTPS dengan benar tetapi mungkin ada masalah ketika Anda menggunakan proxy (misalnya nginx). Anda harus mengatur beberapa header di virtual host nginx https

proxy_set_header   X-HTTPS 1;

dan menggunakan beberapa modul Apache untuk mengatur flag HTTPS dengan benar dengan mencari X-HTTPS dari proxy. Cari mod_fakessl, mod_rpaf, dll.

mikep
sumber
0

Jika Anda menggunakan penyeimbang beban Incapsula, Anda harus menggunakan IRule untuk membuat header khusus untuk server Anda. Saya membuat tajuk HTTP_X_FORWARDED_PROTO yang sama dengan "http" jika port diatur ke 80 dan "https" jika sama dengan 443.

Nadav
sumber
0

Saya akan menambahkan filter global untuk memastikan semua yang saya periksa benar;

function isSSL() {

    $https = filter_input(INPUT_SERVER, 'HTTPS');
    $port = filter_input(INPUT_SERVER, 'SERVER_PORT');
    if ($https) {

        if ($https == 1) {
            return true;
        } elseif ($https == 'on') {
            return true;
        }
    } elseif ($port == '443') {
        return true;
    }

    return false;
}
Rodrigo Manara
sumber
0

Saya memiliki kesempatan untuk melangkah lebih jauh dan menentukan apakah situs yang saya sambungkan memiliki kemampuan SSL (satu proyek menanyakan URL kepada pengguna dan kami perlu memverifikasi mereka telah menginstal paket API kami di situs http atau https).

Inilah fungsi yang saya gunakan - pada dasarnya, cukup panggil URL melalui cURL untuk melihat apakah https berfungsi!

function hasSSL($url) 
{
    // take the URL down to the domain name
    $domain = parse_url($url, PHP_URL_HOST);
    $ch = curl_init('https://' . $domain);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); //its a  HEAD
    curl_setopt($ch, CURLOPT_NOBODY, true);          // no body
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  // in case of redirects
    curl_setopt($ch, CURLOPT_VERBOSE, 0); //turn on if debugging
    curl_setopt($ch, CURLOPT_HEADER, 1);     //head only wanted
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);    // we dont want to wait forever
    curl_exec($ch);
    $header = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($header === 200) {
        return true;
    }
    return false;
}

Ini adalah cara paling andal yang saya temukan tidak hanya untuk mengetahui JIKA Anda menggunakan https (seperti yang ditanyakan), tetapi jika Anda BISA (atau bahkan HARUS) menggunakan https.

CATATAN: adalah mungkin (meskipun tidak benar-benar mungkin ...) bahwa suatu situs dapat memiliki halaman http dan https yang berbeda (jadi jika Anda disuruh menggunakan http, mungkin Anda tidak perlu mengubah ..) Sebagian besar situs adalah sama, dan mungkin harus mengubah rute Anda sendiri, tetapi pemeriksaan tambahan ini telah digunakan (tentu saja seperti yang saya katakan, dalam proyek di mana pengguna memasukkan info situs mereka dan Anda ingin memastikan dari sisi server)

Dukungan CFP
sumber
0

Ini adalah bagaimana saya menemukan menyelesaikan ini

$https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0 ||
        !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
            strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;

return ($https) ? 'https://' : 'http://';
Fhulufhelo Mokhomi
sumber
0

Saya menggunakan saran utama di sini dan merasa terganggu pada "Pemberitahuan PHP" di log ketika HTTPS tidak disetel. Anda dapat menghindarinya dengan menggunakan operator penggabungan nol "??":

if( ($_SERVER['HTTPS'] ?? 'off') == 'off' ) {
    // redirect
}

(Catatan: tidak tersedia sebelum php v7)

garafajon
sumber
-7

Sesuai posting hobodave: "Setel ke nilai yang tidak kosong jika skrip ditanyai melalui protokol HTTPS."

if (!empty($_SERVER['HTTPS']))
{
    $secure_connection = true;
}
pengguna128026
sumber
membuatnya(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie