Come scoprire se stai usando HTTPS senza $ _SERVER ['HTTPS']


191

Ho visto molti tutorial online che dicono che è necessario verificare $_SERVER['HTTPS']se il server è la connessione è protetta con HTTPS. Il mio problema è che su alcuni dei server che uso, $_SERVER['HTTPS']è una variabile indefinita che provoca un errore. C'è un'altra variabile che posso controllare che dovrebbe sempre essere definita?

Per essere chiari, sto attualmente usando questo codice per risolvere se si tratta di una connessione HTTPS:

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

In ogni caso, quei server in cui $ _SERVER ['HTTPS'] non è definito sono in esecuzione su HTTPS?
Freddy,

In realtà, uno di questi è il mio server WAMP di casa. E non credo che sia in esecuzione su HTTPS.
Tyler Carter,

@TylerCarter, Un metodo alternativo è utilizzare i Securecookie. Fai attenzione ai gotcha.
Pacerier

Risposte:


281

Questo dovrebbe funzionare sempre anche quando $_SERVER['HTTPS']non è definito:

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

Il codice è compatibile con IIS.

Dalla documentazione di PHP.net e commenti degli utenti :

1) Impostato su un valore non vuoto se lo script è stato interrogato tramite il protocollo HTTPS.

2) Notare che quando si utilizza ISAPI con IIS, il valore sarà "off" se la richiesta non è stata effettuata tramite il protocollo HTTPS. (Lo stesso comportamento è stato segnalato per IIS7 che esegue PHP come applicazione Fast-CGI).

Inoltre, i server Apache 1.x (e le installazioni non funzionanti) potrebbero non essere stati $_SERVER['HTTPS']definiti anche se connessi in modo sicuro. Sebbene non garantiti, le connessioni sulla porta 443 stanno, per convenzione , probabilmente utilizzando socket sicuri , quindi il controllo della porta aggiuntiva.

Nota aggiuntiva: se esiste un bilanciamento del carico tra il client e il server, questo codice non verifica la connessione tra il client e il bilanciamento del carico, ma la connessione tra il bilanciamento del carico e il server. Per testare la precedente connessione, dovresti testare usando l' HTTP_X_FORWARDED_PROTOintestazione, ma è molto più complesso da fare; vedi gli ultimi commenti sotto questa risposta.


50
Nb: la porta 443 non garantisce che la connessione sia crittografata
ErichBSchulz,

2
@DavidRodrigues Questo non è vero. È possibile utilizzare HTTP / HTTPS su qualsiasi porta desiderata. getservbyname()è solo un riferimento, non una realtà, e non garantisce in alcun modo che HTTPS stia funzionando sulla porta 443.
Brad

1
Ho avuto un piccolo problema con $_SERVER['SERVER_PORT'] !== 443ho dovuto lanciare $_SERVER['SERVER_PORT]un numero intero in questo modo:intval($_SERVER['SERVER_PORT]) !== 443
meconroy

1
1) Il controllo della porta del server è un extra per i server sheetty, meglio rimuoverlo se non è necessario. 2) Notate che è un paragone sciolto nella mia risposta;)
Gras Double,

1
Un altro piccolo problema che ho riscontrato oggi. Il server stava tornando "OFF" non "off" - strtolower($_SERVER['HTTPS']) !== 'off'ha funzionato.
jhummel,

117

La mia soluzione (poiché le condizioni standard [$ _SERVER ['HTTPS'] == 'on'] non funzionano sui server dietro un bilanciamento del carico) sono:

$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: uno standard di fatto per identificare il protocollo di origine di una richiesta HTTP, poiché un proxy inverso (bilanciamento del carico) può comunicare con un server Web utilizzando HTTP anche se la richiesta per il proxy inverso è HTTPS http: //en.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers


4
Questa è la soluzione se si utilizza il proxy inverso di vernice.
Reto Zahner,

1
Il mio problema è stato risolto con questa soluzione. (PHP - AWS Elastic
beanstalk

Questa è la soluzione se si utilizzano i bilanciatori del carico.
Abhishek Saini,

In quale tipo di file stai inserendo questo? Suppongo che questo non sia nel file .htaccess?
Giordania,

5
Questo funziona anche con l'HTTPS gratuito fornito da CloudFlare.
AnthonyVO,

82

Chacha, secondo la documentazione di PHP: "Impostato su un valore non vuoto se lo script è stato interrogato tramite il protocollo HTTPS." Quindi la tua dichiarazione if ci restituirà false in molti casi in cui HTTPS è effettivamente attivo. Ti consigliamo di verificare che $_SERVER['HTTPS']esiste e non è vuoto. Nei casi in cui HTTPS non è impostato correttamente per un determinato server, puoi provare a verificare se $_SERVER['SERVER_PORT'] == 443.

Tuttavia, alcuni server imposteranno anche $_SERVER['HTTPS']un valore non vuoto, quindi assicurati di controllare anche questa variabile.

Riferimento: documentazione per $_SERVERe $HTTP_SERVER_VARS[obsoleto]


12
usa $ _SERVER ['SERVER_PORT'] può essere complicato ... per esempio ispconfig usa la porta 81 come porta sicura, quindi supponiamo che 443 sia la porta "predefinita" per ssl.
Gabriel Sosa,

@Gabriel Sosa - Vero, ma le avvertenze possono essere affrontate caso per caso. La risposta di @ hobodave funzionerà per la maggior parte.
Tim Post

Si noti che questo non funzionerà dietro un proxy inverso. Si potrebbe considerare di controllare HTTP_X_FORWARDED_PROTOo HTTP_X_FORWARDED_SSLpure.
Paolo,

1
Sono d'accordo che l'ultima risorsa dovrebbe essere il numero di porta, quindi ecco il mio controllo: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) che non include affatto il controllo della porta. Sentiti libero di aggiungere. :-)
Roland

@paolo dietro un proxy inverso SetEnvIf X-Forwarded-SSL on HTTPS=onfarà il trucco. Ma questo non funzionerà REQUEST_SCHEMEcome risultato in php sembra meglio usare$_SERVER['HTTPS']
Antony Gibbs

14

Questo funziona anche quando $_SERVER['HTTPS']non è definito

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

2
Sono alcuni server in cui $_SERVER['HTTPS']non è ancora definito https è abilitato. Che ne dici di quello ?
John Max,

1
@JohnMax SERVER_PORTè definito sempre che risolve il problema indefinito diHTTPS
Thamaraiselvam

11

Ho appena avuto un problema in cui stavo eseguendo il server usando Apache mod_ssl, ma un phpinfo () e un var_dump ($ _SERVER) hanno mostrato che PHP pensa ancora che io sia sulla porta 80.

Ecco la mia soluzione alternativa per chiunque abbia lo stesso problema ....

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

La linea degna di nota è la linea SetEnv. Con questo in atto e dopo un riavvio, dovresti avere la variabile d'ambiente HTTPS che hai sempre sognato


5
Meglio essere sicuri che HTTPS funzioni davvero; ciò farà mentire il server se non lo è.
Brad Koch,

Inoltre, è necessario il modulo SetEnv affinché funzioni. È abilitato per impostazione predefinita, ma non si sa mai cosa potrebbe disabilitare un amministratore del server.
toon81,

Molto utile nel caso in cui ci si trovi sulla finestra mobile tramite proxy inverso. Grazie!
dikirill,

9

Facendo la mia funzione dalla lettura di tutti i post precedenti:

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

Se usi Apache puoi sempre contare

$_SERVER["REQUEST_SCHEME"]

per verificare lo schema dell'URL richiesto. Ma, come menzionato in altre risposte, è prudente verificare altri parametri prima di supporre che SSL sia realmente utilizzato.


funziona su XAMPP ma non su centos / apache2 + PHP ... quindi non è affidabile.
Firas Abd Alrahman,

5

La vera risposta: pronta per essere copiata e incollata in uno script [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_URIprotocolè ora corretto e pronto per essere utilizzato; esempio $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Naturalmente, la stringa potrebbe essere sostituita anche con TRUE e FALSE. PV sta per PortalPress Variable in quanto è un copia-incolla diretto che funzionerà sempre. Questo pezzo può essere utilizzato in uno script di produzione.


3

Non penso che l'aggiunta di una porta sia una buona idea, specialmente quando hai molti server con build diverse. che aggiunge solo un'altra cosa da ricordare di cambiare. guardando i documenti penso che l'ultima riga di kaiser sia abbastanza buona, in modo che:

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

sembra abbastanza perfetto.


3

L'unico metodo affidabile è quello descritto da 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://");

Considera quanto segue: Stai usando nginx con fastcgi, per impostazione predefinita (debian, ubuntu) fastgi_params contiene direttiva:

fastcgi_param HTTPS $ https;

se NON stai usando SSL, viene tradotto come valore vuoto, non "off", non 0 e sei condannato.

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


3

Trovo che anche questi parametri siano accettabili e più probabilmente non hanno falsi positivi quando si cambia 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*/}

Questo non ti dice nulla sull'uso di HTTPS con un bilanciamento del carico / proxy.
Brad,

3

Il modo più breve che sto usando:

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

Se si utilizza https, allora $ secure_connection è true.


echo (!empty($_SERVER['HTTPS'])?'https':'http');ti dà httpohttps
Xavi Esteve

, (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
fallo

2

È possibile verificare $_SERVER['SERVER_PORT']come SSL normalmente viene eseguito sulla porta 443, ma questo non è infallibile.


$ _SERVER ['SERVER_PORT'] lo fa comunque.
Tyler Carter,

2

Cosa ne pensi di questo?

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

Si C'è. Se si fa affidamento solo su empty () PHP uscirà con errore se non esiste un indice 'HTTPS'.
toni rmc

3
"empty () è essenzialmente l'equivalente conciso di! isset ($ var) || $ var == false" - php.net/manual/en/function.empty.php
John Magnolia,

2
Hai ragione. Divertente, mi mancava quello. Ho sempre pensato che empty () fallirà se la variabile non esiste.
toni rmc

2

Sul mio server (Ubuntu 14.10, Apache 2.4, php 5.5) la variabile $_SERVER['HTTPS']non è impostata quando lo script php viene caricato tramite https. Non so cosa sia sbagliato. Ma le seguenti righe nel .htaccessfile risolvono questo problema:

RewriteEngine on

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

1

Ecco una funzione riutilizzabile che utilizzo da un po 'di tempo. HTH.

Nota: il valore di HTTPS_PORT (che è una costante personalizzata nel mio codice) può variare in base al tuo ambiente, ad esempio può essere 443 o 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

solo per interesse, Chrome Canary al momento invia

HTTPS : 1

al server e, a seconda della configurazione del server, può significare che si ottiene quanto segue

HTTPS : 1, on

Questo ha rotto la nostra applicazione perché stavamo testando se acceso, cosa che ovviamente non lo è. Al momento, solo il cromo canarino sembra farlo, ma vale la pena notare che le cose del canarino generalmente atterrano nel cromo "normale" poco dopo.


1

Se si utilizza nginx come sistema di bilanciamento del carico, controllare $ _SERVER ['HTTP_HTTPS'] == 1 altri controlli falliranno per 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;

Il codice controlla tutto il possibile e funziona anche sul server Web IIS. Chrome dalla v44 non imposta l'intestazione HTTP: 1, quindi controllare che HTTP_HTTPS sia OK. Se questo codice non corrisponde a https significa che il tuo server web o server proxy non è configurato correttamente. Apache stesso imposta correttamente il flag HTTPS ma può esserci un problema quando si utilizza il proxy (ad esempio nginx). È necessario impostare alcune intestazioni nell'host virtuale nginx https

proxy_set_header   X-HTTPS 1;

e usa alcuni moduli Apache per impostare correttamente il flag HTTPS cercando X-HTTPS dal proxy. Cerca mod_fakessl, mod_rpaf, ecc.


0

Se si utilizza il bilanciamento del carico di Incapsula, è necessario utilizzare un IRule per generare un'intestazione personalizzata per il proprio server. Ho creato un'intestazione HTTP_X_FORWARDED_PROTO che è uguale a "http" se la porta è impostata su 80 e "https" se è uguale a 443.


0

Aggiungerei un filtro globale per garantire che tutto ciò che sto controllando sia corretto;

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

Ho occasione di fare un ulteriore passo avanti e determinare se il sito a cui mi sto collegando è abilitato per SSL (un progetto chiede all'utente il proprio URL e dobbiamo verificare che abbiano installato il nostro pacchetto API su un sito http o https).

Ecco la funzione che uso - in pratica, basta chiamare l'URL tramite cURL per vedere se https funziona!

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

Questo è il modo più affidabile che ho scoperto non solo per scoprire se stai usando https (come fa la domanda), ma se POTREBBE (o anche DOVREBBE) usare https.

NOTA: è possibile (anche se non proprio probabile ...) che un sito possa avere diverse pagine http e https (quindi se ti viene detto di usare http, forse non è necessario cambiare ..) La stragrande maggioranza dei siti sono gli stessi e probabilmente dovrebbero reindirizzare te stesso, ma questo controllo aggiuntivo ha il suo uso (certamente come ho detto, nel progetto in cui l'utente immette le informazioni sul sito e vuoi assicurarti dal lato server)


0

Ecco come trovo risolverlo

$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

Ho usato il suggerimento principale qui e mi sono infastidito dalla "Notifica PHP" nei registri quando HTTPS non è stato impostato. Puoi evitarlo usando l' operatore a coalescenza nulla "??":

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

(Nota: non disponibile prima di php v7)


-7

Secondo il post di hobodave: "Impostato su un valore non vuoto se lo script è stato interrogato tramite il protocollo HTTPS."

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

renderlo(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.