Comment savoir si vous utilisez HTTPS sans $ _SERVER ['HTTPS']

191

J'ai vu de nombreux didacticiels en ligne indiquant que vous devez vérifier $_SERVER['HTTPS']si la connexion du serveur est sécurisée avec HTTPS. Mon problème est que sur certains des serveurs que j'utilise, il $_SERVER['HTTPS']y a une variable non définie qui entraîne une erreur. Y a-t-il une autre variable que je peux vérifier et qui devrait toujours être définie?

Pour être clair, j'utilise actuellement ce code pour résoudre s'il s'agit d'une connexion HTTPS:

if(isset($_SERVER['HTTPS'])) {
    if ($_SERVER['HTTPS'] == "on") {
        $secure_connection = true;
    }
}
Tyler Carter
la source
Par hasard, les serveurs sur lesquels $ _SERVER ['HTTPS'] n'est pas défini fonctionnent sur HTTPS?
Freddy
En fait, l'un d'eux est mon serveur WAMP domestique. Et je ne pense pas qu'il fonctionne sur HTTPS.
Tyler Carter
@TylerCarter, Une méthode alternative consiste à utiliser des Securecookies. Soyez prudent avec les pièges.
Pacerier

Réponses:

281

Cela devrait toujours fonctionner même si $_SERVER['HTTPS']n'est pas défini:

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

Le code est compatible avec IIS.

À partir de la documentation PHP.net et des commentaires des utilisateurs :

1) Définissez sur une valeur non vide si le script a été interrogé via le protocole HTTPS.

2) Notez que lors de l'utilisation d'ISAPI avec IIS, la valeur sera "off" si la demande n'a pas été effectuée via le protocole HTTPS. (Le même comportement a été signalé pour IIS7 exécutant PHP en tant qu'application Fast-CGI).

En outre, les serveurs Apache 1.x (et les installations interrompues) peuvent ne pas avoir été $_SERVER['HTTPS']définis, même s'ils se connectent en toute sécurité. Bien que non garantie, les connexions sur le port 443 sont, par convention , en utilisant probablement des prises sécurisées , d' où le contrôle du port supplémentaire.

Remarque supplémentaire: s'il existe un équilibreur de charge entre le client et votre serveur, ce code ne teste pas la connexion entre le client et l'équilibreur de charge, mais la connexion entre l'équilibreur de charge et votre serveur. Pour tester l'ancienne connexion, vous devriez tester en utilisant l'en- HTTP_X_FORWARDED_PROTOtête, mais c'est beaucoup plus complexe à faire; voir les derniers commentaires ci-dessous cette réponse.

Gras Double
la source
50
Nb: le port 443 ne garantit pas que la connexion est cryptée
ErichBSchulz
2
@DavidRodrigues Ce n'est pas vrai. Vous pouvez utiliser HTTP / HTTPS sur le port de votre choix. getservbyname()n'est qu'une référence, pas une réalité, et ne garantit en aucun cas que HTTPS fonctionne sur le port 443.
Brad
1
J'ai eu un petit problème avec $_SERVER['SERVER_PORT'] !== 443je devais lancer $_SERVER['SERVER_PORT]un entier comme ceci:intval($_SERVER['SERVER_PORT]) !== 443
meconroy
1
1) La vérification du port du serveur est un supplément pour les serveurs sheetty, mieux vaut la supprimer si elle n'est pas nécessaire. 2) Remarquez que c'est une comparaison vague dans ma réponse;)
Gras Double
1
Un autre petit problème que j'ai rencontré aujourd'hui. Le serveur retournait «OFF» et non «off» - a strtolower($_SERVER['HTTPS']) !== 'off'fait l'affaire.
jhummel
117

Ma solution (car les conditions standard [$ _SERVER ['HTTPS'] == 'on'] ne fonctionnent pas sur les serveurs derrière un équilibreur de charge) est:

$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: un standard de facto pour identifier le protocole d'origine d'une requête HTTP, puisqu'un proxy inverse (load balancer) peut communiquer avec un serveur Web en utilisant HTTP même si la requête au proxy inverse est HTTPS http: //en.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers

temuraru
la source
4
C'est la solution si vous utilisez le proxy inverse vernis.
Reto Zahner
1
Mon problème a été résolu avec cette solution. (PHP - AWS Elastic beanstalk)
user4826347
C'est la solution si vous utilisez des équilibreurs de charge.
Abhishek Saini
Dans quel type de fichier placez-vous cela? Je suppose que ce n'est pas dans le fichier .htaccess?
Jordan
5
Cela fonctionne également pour le HTTPS gratuit fourni par CloudFlare.
AnthonyVO
82

Chacha, selon la documentation PHP: "Défini sur une valeur non vide si le script a été interrogé via le protocole HTTPS." Donc, votre instruction if retournera false dans de nombreux cas où HTTPS est effectivement activé. Vous voudrez vérifier qu'il $_SERVER['HTTPS']existe et n'est pas vide. Dans les cas où HTTPS n'est pas correctement défini pour un serveur donné, vous pouvez essayer de vérifier si$_SERVER['SERVER_PORT'] == 443 .

Mais notez que certains serveurs définiront également $_SERVER['HTTPS'] sur une valeur non vide, assurez-vous donc de vérifier également cette variable.

Référence: Documentation pour $_SERVERet $HTTP_SERVER_VARS[obsolète]

Hobodave
la source
12
use $ _SERVER ['SERVER_PORT'] peut être délicat ... par exemple ispconfig utilise le port 81 comme port sécurisé donc disons que 443 est le port "par défaut" pour ssl.
Gabriel Sosa
@Gabriel Sosa - C'est vrai, mais les mises en garde peuvent être adressées au cas par cas. La réponse de @ hobodave fonctionnera pour la plupart.
Tim Post
Notez que cela ne fonctionnera pas derrière un proxy inverse. On pourrait envisager de vérifier HTTP_X_FORWARDED_PROTOou HTTP_X_FORWARDED_SSLaussi.
paolo
1
Je suis d'accord avec le fait que le dernier recours devrait être le numéro de port, alors voici mon chèque: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) qui n'inclut pas du tout le contrôle du port. N'hésitez pas à ajouter. :-)
Roland
@paolo derrière un proxy inverse SetEnvIf X-Forwarded-SSL on HTTPS=onfera l'affaire. Mais cela ne fonctionnera pas, REQUEST_SCHEMEcar php semble préférable d'utiliser$_SERVER['HTTPS']
Antony Gibbs
14

Cela fonctionne également lorsque $_SERVER['HTTPS']n'est pas défini

if( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 ){
    //enable secure connection
}
Thamaraiselvam
la source
2
Ce sont des serveurs sur lesquels $_SERVER['HTTPS']n'est pas encore défini https est activé. Et ça?
John Max
1
@JohnMax SERVER_PORTest défini toujours qui résout le problème indéfini deHTTPS
Thamaraiselvam
11

Je viens d'avoir un problème où j'exécutais le serveur en utilisant Apache mod_ssl, mais un phpinfo () et un var_dump ($ _SERVER) ont montré que PHP pense toujours que je suis sur le port 80.

Voici ma solution de contournement pour toute personne ayant le même problème ...

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

La ligne à noter est la ligne SetEnv. Avec cela en place et après un redémarrage, vous devriez avoir la variable d'environnement HTTPS dont vous avez toujours rêvé

Thomas-Peter
la source
5
Mieux vaut être sûr que HTTPS fonctionne réellement; cela fera que le serveur vous ment si ce n'est pas le cas.
Brad Koch
Vous avez également besoin du module SetEnv pour que cela fonctionne. Il est activé par défaut, mais vous ne savez jamais ce qu'un administrateur de serveur pourrait désactiver.
toon81
Très utile si vous êtes sur le docker via un proxy inverse. Merci!
dikirill
9

Créer ma propre fonction en lisant tous les articles précédents:

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;
}
lingue
la source
7

Si vous utilisez Apache, vous pouvez toujours compter sur

$_SERVER["REQUEST_SCHEME"]

pour vérifier le schéma de l'URL demandée. Mais, comme mentionné dans d'autres réponses, il est prudent de vérifier d'autres paramètres avant de supposer que SSL est vraiment utilisé.

Ed de Almeida
la source
cela fonctionne sur XAMPP mais pas sur centos / apache2 + PHP ... donc il n'est pas fiable.
Firas Abd Alrahman
5

La vraie réponse: prêt pour le copier-coller dans un 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_URIprotocolest maintenant correct et prêt à être utilisé; exemple $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Naturellement, la chaîne pourrait également être remplacée par TRUE et FALSE. PV signifie PortalPress Variable car il s'agit d'un copier-coller direct qui fonctionnera toujours. Cette pièce peut être utilisée dans un script de production.

Igor M. - PortalPress.org
la source
3

Je ne pense pas que l'ajout d'un port soit une bonne idée - surtout lorsque vous avez de nombreux serveurs avec des versions différentes. cela ajoute juste une chose de plus à ne pas oublier pour changer. en regardant les doc, je pense que la dernière ligne de kaisers est assez bonne, de sorte que:

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

semble parfaitement suffisant.

sp3c1
la source
3

La seule méthode fiable est celle décrite par 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://");

Considérez ce qui suit: Vous utilisez nginx avec fastcgi, par défaut (debian, ubuntu) la directive fastgi_params contient:

fastcgi_param HTTPS $ https;

si vous n'utilisez PAS SSL, il est traduit par une valeur vide, pas «off», pas 0 et vous êtes condamné.

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

Galvani
la source
3

Je trouve ces paramètres également acceptables et plus que probablement ils n'ont pas de faux positifs lors du changement de serveur 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*/}
Werezywolf
la source
Cela ne vous dit rien sur l'utilisation HTTPS avec un équilibreur de charge / proxy.
Brad
3

Voie la plus courte que j'utilise:

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

Si https est utilisé, alors $ secure_connection est vrai.

Markus Zeller
la source
echo (!empty($_SERVER['HTTPS'])?'https':'http');vous donne httpouhttps
Xavi Esteve
, faites-le(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
2

Vous pouvez vérifier $_SERVER['SERVER_PORT']que SSL fonctionne normalement sur le port 443, mais ce n'est pas infaillible.

pix0r
la source
Cependant, $ _SERVER ['SERVER_PORT'] le fait.
Tyler Carter
2

Que pensez-vous de cela?

if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
    $scheme = 'https';
else
    $scheme = 'http';
toni rmc
la source
Oui il y a. Si vous comptez uniquement sur empty (), PHP se fermera avec une erreur s'il n'y a pas d'index 'HTTPS'.
toni rmc
3
"empty () est essentiellement l'équivalent concis de! isset ($ var) || $ var == false" - php.net/manual/en/function.empty.php
John Magnolia
2
Vous avez raison. C'est drôle, j'ai raté celui-là. J'ai toujours pensé que empty () échouerait si la variable n'existe pas.
toni rmc
2

Sur mon serveur (Ubuntu 14.10, Apache 2.4, php 5.5), la variable $_SERVER['HTTPS']n'est pas définie lorsque le script php est chargé via https. Je ne sais pas ce qui ne va pas. Mais les lignes suivantes du .htaccessfichier résolvent ce problème:

RewriteEngine on

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

Voici une fonction réutilisable que j'utilise depuis un moment. HTH.

Remarque: La valeur de HTTPS_PORT (qui est une constante personnalisée dans mon code) peut varier selon votre environnement, par exemple elle peut être 443 ou 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
la source
1

juste pour l'intérêt, Chrome Canary envoie en ce moment

HTTPS : 1

au serveur, et selon la façon dont le serveur est configuré peut signifier que vous récupérez les éléments suivants

HTTPS : 1, on

Cela a cassé notre application parce que nous testions si, ce qui n'est évidemment pas le cas. Pour le moment, seul Chrome Canary semble le faire, mais il convient de noter que les objets de Canary atterrissent généralement dans le chrome "normal" peu de temps après.

John Smith
la source
1

Si vous utilisez nginx comme système d'équilibrage de charge, vérifiez $ _SERVER ['HTTP_HTTPS'] == 1, les autres vérifications échoueront pour ssl.

base
la source
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;

Le code vérifie tout ce qui est possible et fonctionne également sur le serveur Web IIS. Chrome depuis la v44 ne définit pas l'en-tête HTTP: 1, donc vérifier HTTP_HTTPS est OK. Si ce code ne correspond pas à https, cela signifie que votre serveur Web ou serveur proxy est mal configuré. Apache lui-même définit correctement l'indicateur HTTPS, mais il peut y avoir un problème lorsque vous utilisez un proxy (par exemple nginx). Vous devez définir un en-tête dans l'hôte virtuel nginx https

proxy_set_header   X-HTTPS 1;

et utilisez un module Apache pour définir correctement l'indicateur HTTPS en recherchant X-HTTPS à partir du proxy. Recherchez mod_fakessl, mod_rpaf, etc.

Mikep
la source
0

Si vous utilisez l'équilibreur de charge d'Incapsula, vous devrez utiliser une IRule pour générer un en-tête personnalisé pour votre serveur. J'ai créé un en-tête HTTP_X_FORWARDED_PROTO qui est égal à "http" si le port est défini sur 80 et "https" s'il est égal à 443.

Nadav
la source
0

J'ajouterais un filtre global pour m'assurer que tout ce que je vérifie est correct;

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
la source
0

J'ai l'occasion d'aller plus loin et de déterminer si le site auquel je me connecte est compatible SSL (un projet demande à l'utilisateur son URL et nous devons vérifier qu'il a installé notre pack API sur un site http ou https).

Voici la fonction que j'utilise - en gros, appelez simplement l'URL via cURL pour voir si https fonctionne!

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

C'est le moyen le plus fiable que j'ai trouvé pour non seulement savoir si vous utilisez https (comme le demande la question), mais si vous POUVEZ (ou même DEVRIEZ) utiliser https.

REMARQUE: il est possible (mais pas vraiment probable ...) qu'un site puisse avoir des pages http et https différentes (donc si on vous dit d'utiliser http, peut-être que vous n'avez pas besoin de changer ..) La grande majorité des sites sont les mêmes, et devraient probablement vous rediriger eux-mêmes, mais cette vérification supplémentaire a son utilité (certainement comme je l'ai dit, dans le projet où l'utilisateur entre les informations de son site et vous voulez vous en assurer du côté serveur)

Assistance CFP
la source
0

C'est comme ça que je trouve résoudre ça

$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
la source
0

J'ai utilisé la suggestion principale ici et je me suis ennuyé de la "notification PHP" dans les journaux lorsque HTTPS n'était pas défini. Vous pouvez l'éviter en utilisant l' opérateur de fusion nul "??":

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

(Remarque: non disponible avant php v7)

garafajon
la source
-7

Selon le post de hobodave: "Définissez une valeur non vide si le script a été interrogé via le protocole HTTPS."

if (!empty($_SERVER['HTTPS']))
{
    $secure_connection = true;
}
user128026
la source
faire(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie