Jak sprawdzić, czy używasz HTTPS bez $ _SERVER ['HTTPS']

190

Widziałem wiele samouczków online, które mówią, że musisz sprawdzić, $_SERVER['HTTPS']czy serwer jest połączony z HTTPS. Mój problem polega na tym, że na niektórych serwerach, których używam, $_SERVER['HTTPS']jest niezdefiniowana zmienna, która powoduje błąd. Czy istnieje inna zmienna, którą mogę sprawdzić i którą zawsze należy zdefiniować?

Żeby było jasne, obecnie używam tego kodu, aby rozwiązać, czy jest to połączenie HTTPS:

if(isset($_SERVER['HTTPS'])) {
    if ($_SERVER['HTTPS'] == "on") {
        $secure_connection = true;
    }
}
Tyler Carter
źródło
Czy istnieje szansa, że ​​serwery, na których $ _SERVER ['HTTPS'] jest niezdefiniowany, działają na HTTPS?
Freddy,
Właściwie jednym z nich jest mój domowy serwer WAMP. I nie wierzę, że działa na HTTPS.
Tyler Carter
@TylerCarter, alternatywną metodą jest użycie Secureplików cookie. Uważaj jednak na gotchas.
Pacerier,

Odpowiedzi:

280

Powinno to zawsze działać, nawet gdy $_SERVER['HTTPS']jest niezdefiniowane:

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

Kod jest zgodny z IIS.

Z dokumentacji PHP.net i komentarzy użytkowników :

1) Ustaw na niepustą wartość, jeśli skrypt był sprawdzany za pomocą protokołu HTTPS.

2) Należy pamiętać, że w przypadku korzystania z ISAPI z IIS wartość będzie „wyłączona”, jeśli żądanie nie zostanie przesłane za pośrednictwem protokołu HTTPS. (Takie samo zachowanie zostało zgłoszone dla IIS7 z uruchomionym PHP jako aplikacja Fast-CGI).

Ponadto serwery Apache 1.x (i uszkodzone instalacje) mogły nie zostać $_SERVER['HTTPS']zdefiniowane, nawet jeśli łączą się bezpiecznie. Chociaż nie jest to gwarantowane, połączenia na porcie 443 są, z reguły , przy użyciu bezpiecznych gniazd , stąd dodatkowe sprawdzenie portu.

Uwaga dodatkowa: jeśli istnieje moduł równoważenia obciążenia między klientem a serwerem, ten kod nie testuje połączenia między klientem a modułem równoważenia obciążenia, ale połączenie między modułem równoważenia obciążenia a serwerem. Aby przetestować poprzednie połączenie, musisz przetestować przy użyciu HTTP_X_FORWARDED_PROTOnagłówka, ale jest to o wiele bardziej skomplikowane; zobacz najnowsze komentarze poniżej tej odpowiedzi.

Gras Double
źródło
50
Uwaga: port 443 nie gwarantuje szyfrowania połączenia
ErichBSchulz
2
@DavidRodrigues To nieprawda. Możesz używać HTTP / HTTPS na dowolnym porcie. getservbyname()jest jedynie odniesieniem, a nie rzeczywistością i nie gwarantuje w żaden sposób, że HTTPS działa na porcie 443.
Brad
1
Miałem mały problem z $_SERVER['SERVER_PORT'] !== 443musiałem rzucić $_SERVER['SERVER_PORT]na liczbę całkowitą taką:intval($_SERVER['SERVER_PORT]) !== 443
meconroy
1
1) Sprawdzanie portu serwera jest dodatkową opcją dla serwerów typu Arkusz, najlepiej je usunąć, jeśli nie jest potrzebne. 2) Zauważ, że w mojej odpowiedzi jest to luźne porównanie;)
Gras Double
1
Jeszcze jeden mały problem, na który natrafiłem dzisiaj. Serwer zwracał „OFF”, a nie „off” - strtolower($_SERVER['HTTPS']) !== 'off'załatwił sprawę.
jhummel
117

Moje rozwiązanie (ponieważ standardowe warunki [$ _SERVER ['HTTPS'] == 'on'] nie działają na serwerach za modułem równoważenia obciążenia):

$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: de facto standard do identyfikacji protokołu źródłowego żądania HTTP, ponieważ zwrotny serwer proxy (moduł równoważenia obciążenia) może komunikować się z serwerem WWW za pomocą protokołu HTTP, nawet jeśli żądanie do zwrotnego serwera proxy to HTTPS http: //en.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers

temuraru
źródło
4
Jest to rozwiązanie, jeśli używasz odwrotnego proxy lakieru.
Reto Zahner
1
Mój problem został rozwiązany dzięki temu rozwiązaniu. (PHP - AWS Elastic beanstalk)
użytkownik4826347
Jest to rozwiązanie, jeśli używasz modułu równoważenia obciążenia.
Abhishek Saini
W jakim typie pliku go umieszczasz? Zakładam, że nie ma tego w pliku .htaccess?
Jordania
5
Działa to również w przypadku bezpłatnego HTTPS zapewnianego przez CloudFlare.
AnthonyVO
82

Chacha, zgodnie z dokumentacją PHP: „Ustaw na niepustą wartość, jeśli skrypt był sprawdzany za pomocą protokołu HTTPS”. Tak więc twoja instrukcja if zwróci false w wielu przypadkach, gdy HTTPS jest rzeczywiście włączony. Będziesz chciał sprawdzić, czy $_SERVER['HTTPS']istnieje i nie jest pusty. W przypadkach, gdy HTTPS nie jest ustawiony poprawnie dla danego serwera, możesz spróbować sprawdzić, czy $_SERVER['SERVER_PORT'] == 443.

Pamiętaj jednak, że niektóre serwery również będą $_SERVER['HTTPS']miały niepustą wartość, więc sprawdź również tę zmienną.

Odniesienie: Dokumentacja $_SERVERi $HTTP_SERVER_VARS[przestarzałe]

hobodave
źródło
12
użyj $ _SERVER ['SERVER_PORT'] może być trudne ... na przykład ispconfig używa portu 81 jako bezpiecznego portu, więc powiedzmy, że 443 jest "domyślnym" portem dla ssl.
Gabriel Sosa
@Gabriel Sosa - To prawda, ale zastrzeżenia można rozwiązywać indywidualnie dla każdego przypadku. Odpowiedź @ hobodave będzie działać dla większości.
Tim Post
Pamiętaj, że to nie będzie działać za odwrotnym proxy. Można rozważyć sprawdzenie HTTP_X_FORWARDED_PROTOlub HTTP_X_FORWARDED_SSLteż.
paolo
1
Zgadzam się, że ostatecznością powinien być numer portu, więc oto mój czek: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) który w ogóle nie obejmuje sprawdzenia portu. Nie krępuj się dodać. :-)
Roland
@ paolo za zwrotnym serwerem proxy załatwi sprawę SetEnvIf X-Forwarded-SSL on HTTPS=on. Ale to nie zadziała, REQUEST_SCHEMEdlatego php wydaje się być lepszy w użyciu$_SERVER['HTTPS']
Antony Gibbs
14

Działa to również wtedy, gdy nie $_SERVER['HTTPS']jest zdefiniowane

if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
    //enable secure connection
}
Thamaraiselvam
źródło
2
Są to niektóre serwery, w których $_SERVER['HTTPS']jeszcze nie zdefiniowano https. Co ty na to ?
John Max
1
@JohnMax SERVER_PORTjest zdefiniowany zawsze, który rozwiązuje nieokreślony problemHTTPS
Thamaraiselvam
11

Właśnie miałem problem z uruchomieniem serwera za pomocą Apache mod_ssl, ale phpinfo () i var_dump ($ _SERVER) pokazały, że PHP wciąż myśli, że jestem na porcie 80.

Oto moje obejście dla każdego, kto ma ten sam problem ...

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

Linia warta odnotowania to linia SetEnv. Mając to na swoim miejscu i po ponownym uruchomieniu, powinieneś mieć zmienną środowiskową HTTPS, o której zawsze marzyłeś

Thomas-Peter
źródło
5
Lepiej upewnij się, że HTTPS naprawdę działa; sprawi, że serwer okłamie cię, jeśli tak nie jest.
Brad Koch
Potrzebny jest również moduł SetEnv, aby to działało. Jest domyślnie włączony, ale nigdy nie wiesz, co administrator serwera może wyłączyć.
toon81
Bardzo przydatne, jeśli korzystasz z dokera poprzez odwrotne proxy. Dzięki!
dikirill
8

Tworzenie własnej funkcji na podstawie czytania wszystkich poprzednich postów:

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;
}
molwa
źródło
7

Jeśli korzystasz z Apache, zawsze możesz liczyć

$_SERVER["REQUEST_SCHEME"]

aby zweryfikować schemat żądanego adresu URL. Ale, jak wspomniano w innych odpowiedziach, rozważne jest sprawdzenie innych parametrów przed założeniem, że SSL jest rzeczywiście używany.

Ed de Almeida
źródło
działa na XAMPP, ale nie na centos / apache2 + PHP ... więc nie jest zaufany.
Firas Abd Alrahman
5

PRAWDZIWA odpowiedź: gotowy do kopiowania i wklejenia do skryptu [config]

/* 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_URIprotocoljest teraz poprawny i gotowy do użycia; przykładem $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Oczywiście ciąg można również zastąpić PRAWDĄ i FAŁSZ. PV oznacza zmienną PortalPress, ponieważ jest to bezpośrednia kopia-wklej, która zawsze będzie działać. Ten kawałek można wykorzystać w skrypcie produkcyjnym.

Igor M. - PortalPress.org
źródło
3

Nie sądzę, aby dodanie portu było dobrym pomysłem - szczególnie, gdy masz wiele serwerów z różnymi kompilacjami. to po prostu dodaje jeszcze jedną rzecz do zapamiętania, aby zmienić. patrząc na dokumenty uważam, że ostatnia linia kaiserów jest całkiem dobra, więc:

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

wydaje się idealnie.

sp3c1
źródło
3

Jedyną niezawodną metodą jest ta opisana przez Igora 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://");

Zastanów się: Używasz nginx z fastcgi, domyślnie (debian, ubuntu) fastgi_params zawiera dyrektywę:

fastcgi_param HTTPS $ https;

jeśli NIE używasz protokołu SSL, zostaje on przetłumaczony jako pusta wartość, a nie „wyłączony”, a nie 0, a ty jesteś skazany.

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

Galvani
źródło
3

Uważam, że te parametry są również akceptowalne i bardziej prawdopodobne jest, że nie mają fałszywych trafień podczas przełączania serwerów sieciowych.

  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*/}
Wilkołak
źródło
Nie mówi to nic o użyciu HTTPS z modułem równoważenia obciążenia / proxy.
Brad
3

Najkrótsza droga, z której korzystam:

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

Jeśli używany jest protokół https, to $ secure_connection jest prawdziwe.

Markus Zeller
źródło
echo (!empty($_SERVER['HTTPS'])?'https':'http');daje ci httplubhttps
Xavi Esteve
, zrób to(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie,
2

Możesz sprawdzić, $_SERVER['SERVER_PORT']ponieważ SSL zwykle działa na porcie 443, ale nie jest to niezawodne.

pix0r
źródło
$ _SERVER ['SERVER_PORT'] robi jednak.
Tyler Carter
2

Co o tym sądzisz?

if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
    $scheme = 'https';
else
    $scheme = 'http';
toni rmc
źródło
Tak jest. Jeśli polegasz tylko na empty (), PHP zakończy działanie z błędem, jeśli nie ma indeksu „HTTPS”.
toni rmc
3
„empty () jest w istocie zwięzłym odpowiednikiem! isset ($ var) || $ var == false” - php.net/manual/en/function.empty.php
John Magnolia
2
Masz rację. Zabawne, że tęskniłem. Zawsze myślałem, że empty () zawiedzie, jeśli zmienna nie istnieje.
toni rmc
2

Na moim serwerze (Ubuntu 14.10, Apache 2.4, php 5.5) zmienna $_SERVER['HTTPS']nie jest ustawiona, gdy skrypt php jest ładowany przez https. Nie wiem co jest nie tak. Ale następujące wiersze w .htaccesspliku rozwiązują ten problem:

RewriteEngine on

RewriteCond %{HTTPS} =on [NC] 
RewriteRule .* - [E=HTTPS:on,NE]
Karry
źródło
1

Oto funkcja wielokrotnego użytku, z której korzystam od dłuższego czasu. HTH.

Uwaga: Wartość HTTPS_PORT (która jest stałą niestandardową w moim kodzie) może się różnić w zależności od środowiska, na przykład może wynosić 443 lub 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
źródło
1

tylko dla zainteresowania, chrom kanarek w tej chwili wysyła

HTTPS : 1

do serwera, a w zależności od konfiguracji serwera może oznaczać, że otrzymasz następujące informacje

HTTPS : 1, on

To zepsuło naszą aplikację, ponieważ testowaliśmy, czy jest włączona, co oczywiście nie jest. W tej chwili wydaje się, że robi to tylko kanarek chromowany, ale warto zauważyć, że rzeczy z kanarka zwykle lądują w „normalnym” chromie chwilę później.

John Smith
źródło
1

Jeśli użyjesz nginx jako systemu równoważenia obciążenia, sprawdź $ _SERVER ['HTTP_HTTPS'] == 1 inne kontrole nie powiodą się dla ssl.

podstawa
źródło
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;

Kod sprawdza wszystko, co możliwe i działa również na serwerze internetowym IIS. Chrome od wersji 44 nie ustawia nagłówka HTTP: 1, więc sprawdzanie HTTP_HTTPS jest OK. Jeśli ten kod nie jest zgodny z https, oznacza to, że twój serwer WWW lub serwer proxy jest źle skonfigurowany. Sam Apache ustawia poprawnie flagę HTTPS, ale może wystąpić problem przy korzystaniu z proxy (np. Nginx). Musisz ustawić nagłówek w wirtualnym hoście nginx https

proxy_set_header   X-HTTPS 1;

i użyj jakiegoś modułu Apache, aby poprawnie ustawić flagę HTTPS, szukając X-HTTPS z serwera proxy. Wyszukaj mod_fakessl, mod_rpaf itp.

mikep
źródło
0

Jeśli używasz modułu równoważenia obciążenia Incapsula, musisz użyć IRule, aby wygenerować niestandardowy nagłówek dla swojego serwera. Utworzyłem nagłówek HTTP_X_FORWARDED_PROTO, który jest równy „http”, jeśli port jest ustawiony na 80, i „https”, jeśli jest równy 443.

Nadav
źródło
0

Dodałbym globalny filtr, aby upewnić się, że wszystko, co sprawdzam, jest prawidłowe;

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
źródło
0

Mam okazję pójść o krok dalej i ustalić, czy witryna, z którą się łączę, obsługuje protokół SSL (jeden projekt pyta użytkownika o jego adres URL i musimy zweryfikować, czy zainstalował nasz pakiet API na stronie http lub https).

Oto funkcja, której używam - po prostu wywołaj adres URL za pomocą cURL, aby sprawdzić, czy https działa!

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

Jest to najbardziej niezawodny sposób, który znalazłem, aby nie tylko dowiedzieć się, JEŻELI używasz protokołu https (jak pyta pytanie), ale jeśli MUSISZ (a nawet POWINIENI) używać protokołu https.

UWAGA: jest możliwe (choć mało prawdopodobne ...), że witryna może mieć różne strony http i https (więc jeśli powiedziano ci, aby używać http, być może nie musisz zmieniać ..) Zdecydowana większość witryn są takie same i prawdopodobnie powinien sam Cię przekierować, ale ta dodatkowa kontrola ma swoje zastosowanie (z pewnością, jak powiedziałem, w projekcie, w którym użytkownik wprowadza informacje o swojej witrynie i chcesz się upewnić po stronie serwera)

Wsparcie CFP
źródło
0

W ten sposób znajduję rozwiązanie tego

$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
źródło
0

Użyłem tutaj głównej sugestii i zirytowało mnie „PHP Notice” w logach, gdy nie ustawiono HTTPS. Można tego uniknąć, używając operatora zerowania koalescencji „??”:

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

(Uwaga: niedostępne przed php v7)

garafajon
źródło
-7

Zgodnie z postem hobodave'a: „Ustaw na niepustą wartość, jeśli skrypt był sprawdzany za pomocą protokołu HTTPS”.

if (!empty($_SERVER['HTTPS']))
{
    $secure_connection = true;
}
użytkownik128026
źródło
zrób to(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie,