Як дізнатися, чи ви використовуєте HTTPS без $ _SERVER ['HTTPS']


190

Я бачив багато навчальних посібників в Інтернеті, де говориться, що вам потрібно перевірити, $_SERVER['HTTPS']чи захищено з'єднання з сервером HTTPS. Моя проблема полягає в тому, що на деяких серверах, якими я користуюся, $_SERVER['HTTPS']є невизначена змінна, що призводить до помилки. Чи є інша змінна, яку я можу перевірити, яку слід завжди визначати?

Щоб зрозуміти, я зараз використовую цей код, щоб вирішити, чи це HTTPS-з'єднання:

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

В будь-якому випадку, сервери, де $ _SERVER ['HTTPS'] не визначено, працюють на HTTPS?
Фредді

Власне, один із них - мій домашній сервер WAMP. І я не вірю, що це працює на HTTPS.
Тайлер Картер

Відповіді:


280

Це має працювати завжди, навіть якщо $_SERVER['HTTPS']це не визначено:

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

Код сумісний з IIS.

З документації та коментарів користувачів PHP.net :

1) Встановіть не порожнє значення, якщо сценарій запитувався через протокол HTTPS.

2) Зауважте, що при використанні ISAPI з IIS значення буде "вимкнено", якщо запит не був зроблений через протокол HTTPS. (Про аналогічну поведінку повідомлялося і для IIS7, що працює PHP, як програма Fast-CGI).

Крім того, сервери Apache 1.x (і зламані установки), можливо, не $_SERVER['HTTPS']визначились, навіть якщо надійно підключитися. Хоча не гарантується, з'єднання по порту 443., по угоді , швидше за все , з допомогою захищених сокетів , отже, додаткові перевірки порту.

Додаткова примітка: якщо між клієнтом і вашим сервером є балансир навантаження, цей код не перевіряє зв'язок між клієнтом і балансиром навантаження, а зв'язок між балансиром навантаження та вашим сервером. Щоб перевірити колишнє з'єднання, вам доведеться протестувати за допомогою HTTP_X_FORWARDED_PROTOзаголовка, але це зробити набагато складніше; дивіться останні коментарі нижче цієї відповіді.


50
Nb: порт 443 не гарантує, що з'єднання зашифроване
ErichBSchulz

2
@DavidRodrigues Це неправда. Ви можете використовувати HTTP / HTTPS через будь-який порт, який ви хочете. getservbyname()є лише посиланням, а не реальністю і жодним чином не гарантує, що HTTPS працює над портом 443.
Бред

1
У мене була невелика проблема з тим, що $_SERVER['SERVER_PORT'] !== 443мені довелося передати $_SERVER['SERVER_PORT]ціле число так:intval($_SERVER['SERVER_PORT]) !== 443
meconroy

1
1) Перевірка порту сервера є додатковою для листових серверів, найкраще видалити її, якщо вона не потрібна. 2) Зауважте, що у моїй відповіді це слабке порівняння;)
Двічі Гра,

1
Ще одне невелике питання, з яким я стикався сьогодні. Сервер повертався "OFF", а не "off" - strtolower($_SERVER['HTTPS']) !== 'off'зробив трюк.
jhummel

117

Моє рішення (оскільки стандартні умови [$ _SERVER ['HTTPS'] == 'on'] не працюють на серверах, що стоять за балансиром навантаження):

$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: фактично стандарт для ідентифікації вихідного протоколу HTTP-запиту, оскільки зворотний проксі (балансир завантаження) може спілкуватися з веб-сервером за допомогою HTTP, навіть якщо запит на зворотний проксі є HTTPS http: //uk.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_request_headers


4
Це рішення, якщо ви використовуєте зворотний проксі-лак.
Рето Захнер

1
Мою проблему вирішено цим рішенням. (PHP - AWS Elastic beanstalk)
користувач4826347

Це рішення, якщо ви використовуєте балансири навантаження.
Абхішек Саїні

У який тип файлу ви це вставляєте? Я припускаю, що цього немає у файлі .htaccess?
Йорданія

5
Це також працює для безкоштовних HTTPS, наданих CloudFlare.
АнтонійВО

82

Chacha, згідно документації на PHP: "Встановіть не порожнє значення, якщо сценарій запитувався через протокол HTTPS." Тож ваша заява if в такому випадку поверне помилкову в багатьох випадках, коли HTTPS дійсно включений. Ви хочете перевірити, чи $_SERVER['HTTPS']існує та є порожнім. У випадках, коли HTTPS встановлений неправильно для даного сервера, ви можете спробувати перевірити, чи є$_SERVER['SERVER_PORT'] == 443 .

Але зауважте, що деякі сервери також встановлять $_SERVER['HTTPS'] не порожнє значення, тому обов'язково перевірте і цю змінну.

Довідка: Документація на $_SERVERта $HTTP_SERVER_VARS[застаріле]


12
використання $ _SERVER ['SERVER_PORT'] може бути складним ... наприклад, ispconfig використовує порт 81 як захищений порт, тому можна сказати, що 443 - порт за замовчуванням для ssl.
Габріель Соса

@Gabriel Sosa - Правда, але застереження можна вирішувати в кожному конкретному випадку. @ відповідь hobodave працюватиме для більшості.
Тім Пост

Зауважте, що це не працюватиме за зворотним проксі. Можна було б розглянути , щоб перевірити HTTP_X_FORWARDED_PROTOчи , HTTP_X_FORWARDED_SSLяк добре.
паоло

1
Я погоджуюся з тим, що останнім засобом повинен бути номер порту, тому ось мій чек: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) який взагалі не включає перевірку портів. Сміливо додайте. :-)
Роланд

@paolo за зворотним проксі-сервером SetEnvIf X-Forwarded-SSL on HTTPS=onзробить трюк. Але це не вийде, REQUEST_SCHEMEтому що php, здається, краще використовувати$_SERVER['HTTPS']
Антоні Гіббс

14

Це також працює, коли $_SERVER['HTTPS']не визначено

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

2
Вони є деякими серверами, де $_SERVER['HTTPS']ще не визначено функцію https. Як щодо цього ?
Джон Макс

1
@JohnMax SERVER_PORTвизначається завжди, що вирішує невизначену проблемуHTTPS
Thamaraiselvam

11

Щойно у мене виникла проблема, коли я запускав сервер за допомогою Apache mod_ssl, але phpinfo () та var_dump ($ _SERVER) показали, що PHP все ще думає, що я перебуваю на порту 80.

Ось мій спосіб вирішення тих, хто з тим самим питанням ....

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

Лінія, яку варто відзначити, - це лінія SetEnv. Якщо це на місці та після перезавантаження, ви повинні мати змінну середовища HTTPS, про яку завжди мріяли


5
Будьте впевнені, що HTTPS справді працює; це змусить сервер брехати вам, якщо його немає.
Бред Кох

Також для цього вам потрібен модуль SetEnv. Він включений за замовчуванням, але ви ніколи не знаєте, що може відключити адміністратор сервера.
toon81

Дуже корисно у випадку, якщо ви перебуваєте на докері через зворотний проксі. Дякую!
dikirill

8

Як зробити власну функцію від читання всіх попередніх публікацій:

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

Якщо ви використовуєте Apache, ви завжди можете розраховувати

$_SERVER["REQUEST_SCHEME"]

щоб перевірити схему запитуваної URL-адреси. Але, як зазначено в інших відповідях, доцільно перевірити інші параметри, перш ніж припускати, що SSL справді використовується.


він працює на XAMPP, але не на centos / apache2 + PHP ... тому йому не довіряють.
Фірас Абд Алрахман

5

РЕАЛЬНА відповідь: готова до копіювання-вставки в сценарій [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тепер правильний і готовий до використання; приклад $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Природно, рядок може бути замінений на TRUE та FALSE також. PV означає "PortalPress Variable", оскільки це пряма копія-паста, яка завжди працюватиме. Цей твір може бути використаний у виробничому сценарії.


3

Я не думаю, що додавання порту є гарною ідеєю - особливо коли у вас багато серверів з різними складаннями. що лише додає ще одну річ, яку потрібно пам’ятати, щоб змінити. дивлячись на doc, я думаю, що останній рядок кайзерів досить хороший, так що:

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

здається досить ідеально.


3

Єдиний надійний метод - той, який описав Ігор М.

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

Розглянемо наступне: Ви використовуєте nginx з fastcgi, за замовчуванням (debian, ubuntu) fastgi_params містять директиву:

fastcgi_param HTTPS $ https;

якщо ви НЕ використовуєте SSL, він переводиться як порожнє значення, а не "вимкнено", не 0, і ви приречені.

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


3

Я вважаю ці парами прийнятними, і, швидше за все, вони не мають помилкових позитивних змін при переключенні веб-серверів.

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

Це нічого не говорить про використання HTTPS з балансиром навантаження / проксі.
Бред

3

Найкоротшим способом я користуюся:

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

Якщо використовується https, значить $ secure_connection.


echo (!empty($_SERVER['HTTPS'])?'https':'http');дарує вам httpабоhttps
Хаві Естеве

, зробити це(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie

2

Ви можете перевірити, $_SERVER['SERVER_PORT']як SSL зазвичай працює на порту 443, але це не є надійним.


$ _SERVER ['SERVER_PORT'] робить це.
Тайлер Картер

2

Що ти думаєш про це?

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

Так, є. Якщо ви покладаєтесь лише на порожній (), PHP вийде з помилкою, якщо немає індексу HTTPS.
toni rmc

3
"empty () по суті є лаконічним еквівалентом! isset ($ var) || $ var == false" - php.net/manual/en/function.empty.php
Джон Магнолія

2
Ти правий. Смішно, я пропустив це. Я завжди думав, що порожній () не вдасться, якщо змінної не існує.
toni rmc

2

На моєму сервері (Ubuntu 14.10, Apache 2.4, php 5.5) змінна $_SERVER['HTTPS']не встановлюється, коли php-скрипт завантажується через https. Я не знаю, що не так. Але наступні рядки у .htaccessфайлі вирішують цю проблему:

RewriteEngine on

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

1

Ось функція для повторного використання, яку я використовую деякий час. HTH.

Примітка. Значення HTTPS_PORT (яке є власною константою в моєму коді) може змінюватись у вашому середовищі, наприклад, воно може становити 443 або 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

просто для інтересу, хромована канарка в даний момент посилає

HTTPS : 1

на сервер, і залежно від налаштування сервера може означати, що ви отримуєте назад наступне

HTTPS : 1, on

Це зламало наше додаток, тому що ми тестували, якщо на, що, очевидно, це не так. На даний момент, здається, це робить лише хромована канарка, але варто відзначити, що речі з канарки зазвичай приземляються на "звичайний" хром на короткий час.


1

Якщо ви використовуєте nginx як систему збалансування навантаження, перевірте $ _SERVER ['HTTP_HTTPS'] == 1 інші перевірки не вдасться виконати 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;

Код перевіряє все можливе і працює також на веб-сервері IIS. Chrome, оскільки v44 не встановлює HTTP заголовка: 1, тому перевірка HTTP_HTTPS в порядку. Якщо цей код не відповідає https, це означає, що ваш веб-сервер або проксі-сервер погано налаштовані. Сам Apache правильно встановлює прапор HTTPS, але при використанні проксі може виникнути проблеми (наприклад, nginx). Ви повинні встановити деякий заголовок у nginx https віртуальному хості

proxy_set_header   X-HTTPS 1;

і використовувати деякий модуль Apache, щоб правильно встановити прапор HTTPS, шукаючи X-HTTPS з проксі. Пошук mod_fakessl, mod_rpaf тощо.


0

Якщо ви використовуєте балансир завантаження Incapsula, вам потрібно буде використовувати IRule для створення користувальницького заголовка для вашого сервера. Я створив заголовок HTTP_X_FORWARDED_PROTO, який дорівнює або "http", якщо для порту встановлено 80, і "https", якщо він дорівнює 443.


0

Я би додав глобальний фільтр, щоб переконатися, що все, що я перевіряю, є правильним;

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

У мене є привід піти на крок далі і визначити, чи може сайт, до якого я підключаюсь, підтримує SSL (один проект запитує у користувача його URL, і нам потрібно перевірити, чи встановлено він наш пакет API на веб-сайті http або https).

Ось функція, яку я використовую - в основному просто зателефонуйте за URL-адресою через CURL, щоб побачити, чи працює https!

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

Це найнадійніший спосіб, який я виявив не лише, якщо ви використовуєте https (як задається питанням), але якщо ви МОЖЕТЕ (або навіть ПОТРІБНО) використовувати https.

ПРИМІТКА. Можливо (хоча це не дуже ймовірно ...), що на сайті можуть бути різні сторінки http та https (тому якщо вам скажуть використовувати http, можливо, вам не потрібно змінювати ..) Переважна більшість сайтів такі самі, і, ймовірно, вам слід перенаправити вас самих, але ця додаткова перевірка має своє використання (звичайно, як я вже сказав, в проекті, де користувач вводить інформацію про свій сайт, і ви хочете переконатися з сервера)


0

Ось як я знаходжу це вирішити

$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

Я використав тут головну пропозицію і роздратувався у "Повідомленні PHP" у журналах, коли HTTPS не було встановлено. Ви можете уникнути цього, скориставшись оператором "??":

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

(Примітка: недоступно до php v7)


-7

Відповідно до публікації hobodave: "Встановіть не порожнє значення, якщо сценарій запитувався через протокол HTTPS."

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

зробити це(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.