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

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

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.


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


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]


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
}

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ś


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

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.


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.


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.


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


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*/}

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.


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.


$ _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';

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]

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

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.


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.


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.


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.


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

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)


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://';

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)


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

zrób to(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
— Tivie,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.