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.