So finden Sie heraus, ob Sie HTTPS ohne $ _SERVER ['HTTPS'] verwenden


190

Ich habe viele Online-Tutorials gesehen, die besagen, dass Sie überprüfen müssen, $_SERVER['HTTPS']ob die Verbindung des Servers mit HTTPS gesichert ist. Mein Problem ist, dass auf einigen der von mir verwendeten Server $_SERVER['HTTPS']eine undefinierte Variable ist, die zu einem Fehler führt. Gibt es eine andere Variable, die ich überprüfen kann und die immer definiert werden sollte?

Um ganz klar zu sein, ich verwende derzeit diesen Code, um zu beheben, ob es sich um eine HTTPS-Verbindung handelt:

if(isset($_SERVER['HTTPS'])) {
    if ($_SERVER['HTTPS'] == "on") {
        $secure_connection = true;
    }
}

Möglicherweise laufen die Server, auf denen $ _SERVER ['HTTPS'] undefiniert ist, auf HTTPS?
Freddy

Tatsächlich ist einer von ihnen mein WAMP-Heimserver. Und ich glaube nicht, dass es auf HTTPS läuft.
Tyler Carter

@ TylerCarter, Eine alternative Methode ist die Verwendung von SecureCookies. Sei aber vorsichtig mit den Fallstricken.
Pacerier

Antworten:


280

Dies sollte immer funktionieren, auch wenn $_SERVER['HTTPS']es nicht definiert ist:

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

Der Code ist mit IIS kompatibel.

Aus der PHP.net-Dokumentation und den Benutzerkommentaren :

1) Setzen Sie einen nicht leeren Wert, wenn das Skript über das HTTPS-Protokoll abgefragt wurde.

2) Beachten Sie, dass bei Verwendung von ISAPI mit IIS der Wert "Aus" ist, wenn die Anforderung nicht über das HTTPS-Protokoll gestellt wurde. (Das gleiche Verhalten wurde für IIS7 gemeldet, auf dem PHP als Fast-CGI-Anwendung ausgeführt wird.)

Außerdem haben Apache 1.x-Server (und fehlerhafte Installationen) möglicherweise keine $_SERVER['HTTPS']Definition, selbst wenn eine sichere Verbindung besteht. Obwohl nicht garantiert, verwenden Verbindungen an Port 443 gemäß Konvention wahrscheinlich sichere Sockets , daher die zusätzliche Portprüfung.

Zusätzlicher Hinweis: Wenn zwischen dem Client und Ihrem Server ein Load Balancer besteht, testet dieser Code nicht die Verbindung zwischen dem Client und dem Load Balancer, sondern die Verbindung zwischen dem Load Balancer und Ihrem Server. Um die frühere Verbindung zu testen, müssten Sie den HTTP_X_FORWARDED_PROTOHeader testen , dies ist jedoch viel komplexer. Siehe die neuesten Kommentare unter dieser Antwort.


50
Nb: Port 443 garantiert nicht, dass die Verbindung verschlüsselt ist
ErichBSchulz

2
@ DavidRodrigues Das stimmt nicht. Sie können HTTP / HTTPS über einen beliebigen Port verwenden. getservbyname()ist nur eine Referenz, keine Realität und garantiert in keiner Weise, dass HTTPS über Port 443 ausgeführt wird.
Brad

1
Ich hatte ein kleines Problem damit, dass $_SERVER['SERVER_PORT'] !== 443ich $_SERVER['SERVER_PORT]auf eine ganze Zahl wie intval($_SERVER['SERVER_PORT]) !== 443
folgt umwandeln musste

1
1) Die Überprüfung des Serverports ist eine zusätzliche Option für Sheetty-Server. Entfernen Sie sie am besten, wenn sie nicht benötigt wird. 2) Beachten Sie, dass es ein loser Vergleich in meiner Antwort ist;)
Gras Double

1
Ein weiteres kleines Problem, auf das ich heute gestoßen bin. Der Server gab "AUS" und nicht "Aus" zurück - strtolower($_SERVER['HTTPS']) !== 'off'hat den Trick gemacht.
Jhummel

116

Meine Lösung (da die Standardbedingungen [$ _SERVER ['HTTPS'] == 'on'] auf Servern hinter einem Load Balancer nicht funktionieren) lautet:

$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: Ein De-facto- Standard zum Identifizieren des Ursprungsprotokolls einer HTTP-Anforderung, da ein Reverse-Proxy (Load Balancer) über HTTP mit einem Webserver kommunizieren kann, selbst wenn die Anforderung an den Reverse-Proxy HTTPS http: //en.wikipedia lautet . org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers


4
Dies ist die Lösung, wenn Sie den Lack-Reverse-Proxy verwenden.
Reto Zahner

1
Mein Problem wurde mit dieser Lösung behoben. (PHP - AWS Elastic Beanstalk)
Benutzer4826347

Dies ist die Lösung, wenn Sie Load Balancer verwenden.
Abhishek Saini

In welche Art von Datei stecken Sie diese? Ich gehe davon aus, dass dies nicht in der .htaccess-Datei enthalten ist.
Jordanien

5
Dies funktioniert auch für das kostenlose HTTPS von CloudFlare.
AnthonyVO

82

Chacha, laut PHP-Dokumentation: "Auf einen nicht leeren Wert setzen, wenn das Skript über das HTTPS-Protokoll abgefragt wurde." Ihre if-Anweisung dort wird also in vielen Fällen, in denen HTTPS tatsächlich aktiviert ist, false zurückgeben. Sie möchten überprüfen, ob $_SERVER['HTTPS']vorhanden und nicht leer ist. In Fällen, in denen HTTPS für einen bestimmten Server nicht richtig eingestellt ist, können Sie versuchen, zu überprüfen, ob $_SERVER['SERVER_PORT'] == 443.

Beachten Sie jedoch, dass einige Server auch $_SERVER['HTTPS']einen nicht leeren Wert festlegen. Überprüfen Sie daher auch diese Variable.

Referenz: Dokumentation für $_SERVERund $HTTP_SERVER_VARS[veraltet]


12
Die Verwendung von $ _SERVER ['SERVER_PORT'] kann schwierig sein. Beispielsweise verwendet ispconfig Port 81 als sicheren Port. Nehmen wir also an, 443 ist der "Standard" -Port für SSL.
Gabriel Sosa

@ Gabriel Sosa - Stimmt, aber Vorbehalte können von Fall zu Fall behoben werden. Die Antwort von @ hobodave wird für die meisten funktionieren.
Tim Post

Beachten Sie, dass dies hinter einem Reverse-Proxy nicht funktioniert. Man könnte in Betracht ziehen, dies zu überprüfen HTTP_X_FORWARDED_PROTOoder HTTP_X_FORWARDED_SSLauch.
Paolo

1
Ich bin damit einverstanden, dass der letzte Ausweg die Portnummer sein sollte. Hier ist mein Scheck: Der (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) Portcheck ist überhaupt nicht enthalten. Fühlen Sie sich frei hinzuzufügen. :-)
Roland

@paolo hinter einem Reverse-Proxy SetEnvIf X-Forwarded-SSL on HTTPS=onwird den Trick machen. Aber dies wird nicht funktionieren, REQUEST_SCHEMEda PHP besser zu verwenden scheint$_SERVER['HTTPS']
Antony Gibbs

14

Dies funktioniert auch, wenn $_SERVER['HTTPS']es nicht definiert ist

if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
    //enable secure connection
}

2
Dies sind einige Server, auf denen $_SERVER['HTTPS']noch kein https aktiviert ist. Wie wär es damit ?
John Max

1
@ JohnMax SERVER_PORTist immer definiert, die das undefinierte Problem vonHTTPS
Thamaraiselvam

11

Ich hatte gerade ein Problem, bei dem ich den Server mit Apache mod_ssl ausführte, aber ein phpinfo () und ein var_dump ($ _SERVER) zeigten, dass PHP immer noch glaubt, ich sei auf Port 80.

Hier ist meine Problemumgehung für alle, die das gleiche Problem haben ...

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

Die erwähnenswerte Zeile ist die SetEnv-Zeile. Wenn dies vorhanden ist und Sie nach einem Neustart die HTTPS-Umgebungsvariable haben, von der Sie immer geträumt haben


5
Stellen Sie besser sicher, dass HTTPS wirklich funktioniert. das wird den Server dazu bringen, dich anzulügen, wenn es nicht so ist.
Brad Koch

Außerdem benötigen Sie das SetEnv-Modul, damit dies funktioniert. Es ist standardmäßig aktiviert, aber Sie wissen nie, was ein Serveradministrator möglicherweise deaktiviert.
Toon81

Sehr nützlich für den Fall, dass Sie über den Reverse-Proxy im Docker sind. Vielen Dank!
Dikirill

8

Ich mache meine eigene Funktion, indem ich alle vorherigen Beiträge lese:

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

Wenn Sie Apache verwenden, können Sie sich immer darauf verlassen

$_SERVER["REQUEST_SCHEME"]

um das Schema der angeforderten URL zu überprüfen. Wie in anderen Antworten erwähnt, ist es jedoch ratsam, andere Parameter zu überprüfen, bevor davon ausgegangen wird, dass SSL tatsächlich verwendet wird.


Es funktioniert unter XAMPP, aber nicht unter Centos / Apache2 + PHP. Es ist also nicht vertrauenswürdig.
Firas Abd Alrahman

5

Die WIRKLICHE Antwort: Bereit zum Kopieren und Einfügen in ein [Konfigurations] -Skript

/* 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_URIprotocolist jetzt korrekt und einsatzbereit; Beispiel $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Natürlich könnte die Zeichenfolge auch durch TRUE und FALSE ersetzt werden. PV steht für PortalPress Variable, da es sich um ein direktes Kopieren und Einfügen handelt, das immer funktioniert. Dieses Stück kann in einem Produktionsskript verwendet werden.


3

Ich denke nicht, dass das Hinzufügen eines Ports eine gute Idee ist - besonders wenn Sie viele Server mit unterschiedlichen Builds haben. das fügt nur noch eine Sache hinzu, an die man sich erinnern muss, um sich zu ändern. Wenn ich mir die Dokumente ansehe, denke ich, dass die letzte Reihe von Kaisern ziemlich gut ist, so dass:

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

scheint perfekt genug.


3

Die einzig verlässliche Methode ist die von Igor M. beschriebene.

$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://");

Beachten Sie Folgendes: Sie verwenden nginx mit fastcgi. Standardmäßig (debian, ubuntu) enthalten fastgi_params Direktiven:

fastcgi_param HTTPS $ https;

Wenn Sie NICHT SSL verwenden, wird es als leerer Wert übersetzt, nicht als "Aus", nicht als 0, und Sie sind zum Scheitern verurteilt.

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


3

Ich finde diese Parameter ebenfalls akzeptabel und habe beim Wechseln von Webservern höchstwahrscheinlich keine Fehlalarme.

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

Dies sagt nichts über die HTTPS-Verwendung mit einem Load Balancer / Proxy aus.
Brad

3

Der kürzeste Weg, den ich benutze:

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

Wenn https verwendet wird, ist $ secure_connection true.


echo (!empty($_SERVER['HTTPS'])?'https':'http');gibt Ihnen httpoderhttps
Xavi Esteve

, mach es(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie

2

Sie können überprüfen, ob $_SERVER['SERVER_PORT']SSL normalerweise auf Port 443 ausgeführt wird, dies ist jedoch nicht kinderleicht.


$ _SERVER ['SERVER_PORT'] tut dies jedoch.
Tyler Carter

2

Was denkst du über dies?

if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
    $scheme = 'https';
else
    $scheme = 'http';

Ja da ist. Wenn Sie sich nur auf empty () verlassen, wird PHP mit einem Fehler beendet, wenn kein 'HTTPS'-Index vorhanden ist.
Toni Rmc

3
"empty () ist im Wesentlichen das prägnante Äquivalent zu! isset ($ var) || $ var == false" - php.net/manual/en/function.empty.php
John Magnolia

2
Du hast recht. Komisch, dass ich das verpasst habe. Ich habe immer gedacht, dass empty () fehlschlägt, wenn keine Variable existiert.
Toni Rmc

2

Auf meinem Server (Ubuntu 14.10, Apache 2.4, PHP 5.5) ist die Variable $_SERVER['HTTPS']nicht festgelegt, wenn das PHP-Skript über https geladen wird. Ich weiß nicht, was los ist. Die folgenden Zeilen in der .htaccessDatei beheben dieses Problem:

RewriteEngine on

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

1

Hier ist eine wiederverwendbare Funktion, die ich seit einiger Zeit benutze. HTH.

Hinweis: Der Wert von HTTPS_PORT (eine benutzerdefinierte Konstante in meinem Code) kann je nach Umgebung variieren, z. B. 443 oder 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

Nur aus Interesse sendet Chromkanarienvogel im Moment

HTTPS : 1

Je nachdem, wie der Server konfiguriert ist, kann dies bedeuten, dass Sie Folgendes zurückerhalten

HTTPS : 1, on

Dies hat unsere Anwendung beschädigt, weil wir getestet haben, ob sie aktiviert ist, was offensichtlich nicht der Fall ist. Im Moment scheint dies nur Chromkanarienvogel zu tun, aber es ist erwähnenswert, dass Kanariensachen kurze Zeit später im Allgemeinen in "normalem" Chrom landen.


1

Wenn Sie nginx als Lastausgleichssystem verwenden, überprüfen Sie $ _SERVER ['HTTP_HTTPS'] == 1 andere Überprüfungen schlagen für ssl fehl.


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;

Code überprüft alles Mögliche und funktioniert auch auf dem IIS-Webserver. Chrome seit v44 setzt den Header HTTP: 1 nicht, daher ist die Überprüfung von HTTP_HTTPS in Ordnung. Wenn dieser Code nicht mit https übereinstimmt, bedeutet dies, dass Ihr Webserver oder Proxyserver schlecht konfiguriert ist. Apache selbst setzt das HTTPS-Flag korrekt, es kann jedoch zu Problemen kommen, wenn Sie einen Proxy verwenden (z. B. Nginx). Sie müssen einen Header im virtuellen Host von nginx https festlegen

proxy_set_header   X-HTTPS 1;

Verwenden Sie ein Apache-Modul, um das HTTPS-Flag korrekt zu setzen, indem Sie vom Proxy aus nach X-HTTPS suchen. Suche nach mod_fakessl, mod_rpaf usw.


0

Wenn Sie den Load Balancer von Incapsula verwenden, müssen Sie eine IRule verwenden, um einen benutzerdefinierten Header für Ihren Server zu generieren. Ich habe einen HTTP_X_FORWARDED_PROTO-Header erstellt, der entweder "http" entspricht, wenn der Port auf 80 gesetzt ist, und "https", wenn er 443 entspricht.


0

Ich würde einen globalen Filter hinzufügen, um sicherzustellen, dass alles, was ich überprüfe, korrekt ist.

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

Ich habe Gelegenheit, noch einen Schritt weiter zu gehen und festzustellen, ob die Site, zu der ich eine Verbindung herstelle, SSL-fähig ist (ein Projekt fragt den Benutzer nach seiner URL und wir müssen überprüfen, ob er unser API-Paket auf einer http- oder https-Site installiert hat).

Hier ist die Funktion, die ich benutze - im Grunde rufe einfach die URL über cURL auf, um zu sehen, ob https funktioniert!

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

Dies ist der zuverlässigste Weg, den ich gefunden habe, um nicht nur herauszufinden, ob Sie https verwenden (wie in der Frage gestellt), sondern auch, ob Sie https verwenden KÖNNEN (oder sogar SOLLTEN).

HINWEIS: Es ist möglich (obwohl nicht sehr wahrscheinlich ...), dass eine Site unterschiedliche http- und https-Seiten hat (wenn Sie also aufgefordert werden, http zu verwenden, müssen Sie möglicherweise keine Änderungen vornehmen ..). Die überwiegende Mehrheit der Sites sind die gleichen und sollten Sie wahrscheinlich selbst umleiten, aber diese zusätzliche Überprüfung hat ihre Verwendung (sicherlich, wie gesagt, in dem Projekt, in dem der Benutzer seine Site-Informationen eingibt und Sie dies von der Serverseite aus sicherstellen möchten).


0

So finde ich das zu lösen

$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

Ich habe den Hauptvorschlag hier verwendet und mich über den "PHP-Hinweis" in den Protokollen geärgert, als HTTPS nicht eingestellt war. Sie können dies vermeiden, indem Sie den Null-Koaleszenz-Operator "??" verwenden:

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

(Hinweis: nicht verfügbar vor PHP v7)


-7

Gemäß dem Beitrag von hobodave: "Auf einen nicht leeren Wert setzen, wenn das Skript über das HTTPS-Protokoll abgefragt wurde."

if (!empty($_SERVER['HTTPS']))
{
    $secure_connection = true;
}

machen Sie es(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.