Cách tìm hiểu xem bạn có đang sử dụng HTTPS mà không có $ _SERVER ['HTTPS']


190

Tôi đã thấy nhiều hướng dẫn trực tuyến nói rằng bạn cần kiểm tra xem $_SERVER['HTTPS']máy chủ có kết nối được bảo mật với HTTPS không. Vấn đề của tôi là trên một số máy chủ tôi sử dụng, $_SERVER['HTTPS']là một biến không xác định dẫn đến lỗi. Có một biến khác tôi có thể kiểm tra phải luôn được xác định không?

Để rõ ràng, tôi hiện đang sử dụng mã này để giải quyết nếu đó là kết nối HTTPS:

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

Trong mọi trường hợp, những máy chủ có $ _SERVER ['HTTPS'] không được xác định đang chạy trên HTTPS?
Freddy

Trên thực tế, một trong số đó là máy chủ WAMP nhà tôi. Và tôi không tin rằng nó đang chạy trên HTTPS.
Tyler Carter

@TylerCarter, Một phương pháp khác là sử dụng Securecookie. Hãy cẩn thận với gotchas mặc dù.
Pacerier

Câu trả lời:


280

Điều này sẽ luôn hoạt động ngay cả khi $_SERVER['HTTPS']không xác định:

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

Mã này tương thích với IIS.

Từ tài liệu PHP.net và ý kiến ​​người dùng :

1) Đặt thành giá trị không trống nếu tập lệnh được truy vấn thông qua giao thức HTTPS.

2) Lưu ý rằng khi sử dụng ISAPI với IIS, giá trị sẽ bị "tắt" nếu yêu cầu không được thực hiện thông qua giao thức HTTPS. (Hành vi tương tự đã được báo cáo cho IIS7 chạy PHP dưới dạng ứng dụng Fast-CGI).

Ngoài ra, các máy chủ Apache 1.x (và các cài đặt bị hỏng) có thể không $_SERVER['HTTPS']được xác định ngay cả khi kết nối an toàn. Mặc dù không được bảo đảm, các kết nối trên cổng 443, theo quy ước , có khả năng sử dụng các ổ cắm an toàn , do đó kiểm tra cổng bổ sung.

Lưu ý thêm: nếu có bộ cân bằng tải giữa máy khách và máy chủ của bạn, mã này không kiểm tra kết nối giữa máy khách và bộ cân bằng tải, mà là kết nối giữa bộ cân bằng tải và máy chủ của bạn. Để kiểm tra kết nối cũ, bạn sẽ phải kiểm tra bằng HTTP_X_FORWARDED_PROTOtiêu đề, nhưng việc này phức tạp hơn nhiều; xem ý kiến ​​mới nhất bên dưới câu trả lời này.


50
Nb: cổng 443 không đảm bảo kết nối được mã hóa
ErichBSchulz

2
@DavidRodrigues Điều đó không đúng. Bạn có thể sử dụng HTTP / HTTPS trên bất kỳ cổng nào bạn muốn. getservbyname()chỉ là một tài liệu tham khảo, không phải là thực tế và không đảm bảo rằng HTTPS đang chạy trên cổng 443.
Brad

1
Tôi gặp một vấn đề nhỏ với việc $_SERVER['SERVER_PORT'] !== 443tôi phải chuyển $_SERVER['SERVER_PORT]sang một số nguyên như vậy:intval($_SERVER['SERVER_PORT]) !== 443
meconroy

1
1) Kiểm tra cổng máy chủ là một bổ sung cho các máy chủ tuyệt vời, tốt nhất là loại bỏ nó nếu không cần thiết. 2) Lưu ý rằng đó là một so sánh lỏng lẻo trong câu trả lời của tôi;)
Gras Double

1
Một vấn đề nhỏ nữa tôi gặp phải hôm nay. Máy chủ đã trả về 'TẮT' chứ không phải 'tắt' - strtolower($_SERVER['HTTPS']) !== 'off'đã thực hiện thủ thuật này.
jhummel

116

Giải pháp của tôi (vì các điều kiện tiêu chuẩn [$ _SERVER ['HTTPS'] == 'on'] không hoạt động trên các máy chủ phía sau bộ cân bằng tải) là:

$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: một tiêu chuẩn thực tế để xác định giao thức khởi tạo của một yêu cầu HTTP, vì một proxy ngược (bộ cân bằng tải) có thể giao tiếp với máy chủ web bằng HTTP ngay cả khi yêu cầu tới proxy ngược là HTTPS http: //en.wikipedia. org / wiki / List_of_HTTP_header_fields # Common_non-standard_Vquest_headers


4
Đây là giải pháp nếu bạn sử dụng proxy ngược véc ni.
Reto Zahner

1
Vấn đề của tôi đã được giải quyết với giải pháp này. (PHP - Đậu
đũa

Đây là giải pháp nếu bạn sử dụng cân bằng tải.
Abhishek Saini

Loại tập tin nào bạn đang dán này? Tôi giả sử điều này không có trong tập tin .htaccess?
Jordan

5
Điều này cũng hoạt động cho HTTPS miễn phí được cung cấp bởi CloudFlare.
AnthonyVO

82

Chacha, theo tài liệu PHP: "Đặt thành giá trị không trống nếu tập lệnh được truy vấn thông qua giao thức HTTPS." Vì vậy, câu lệnh if của bạn sẽ trả về false trong nhiều trường hợp HTTPS thực sự được bật. Bạn sẽ muốn xác minh rằng $_SERVER['HTTPS']tồn tại và không trống. Trong trường hợp HTTPS không được đặt chính xác cho một máy chủ nhất định, bạn có thể thử kiểm tra xem $_SERVER['SERVER_PORT'] == 443.

Nhưng lưu ý rằng một số máy chủ cũng sẽ được đặt thành $_SERVER['HTTPS']giá trị không trống, do đó hãy chắc chắn kiểm tra biến này.

Tham khảo: Tài liệu cho $_SERVER$HTTP_SERVER_VARS[không dùng nữa]


12
sử dụng $ _SERVER ['SERVER_PORT'] có thể khó khăn ... ví dụ ispconfig sử dụng cổng 81 làm cổng bảo mật, vì vậy hãy nói rằng 443 là cổng "mặc định" cho ssl.
Gabriel Sosa

@Gabriel Sosa - Đúng, nhưng hãy cẩn thận trong từng trường hợp. @ hobodave 'câu trả lời sẽ làm việc cho hầu hết.
Tim Post

Lưu ý rằng điều này sẽ không hoạt động đằng sau một proxy ngược. Người ta có thể xem xét để kiểm tra HTTP_X_FORWARDED_PROTOhoặc HTTP_X_FORWARDED_SSLlà tốt.
paolo

1
Tôi đồng ý với biện pháp cuối cùng là số cổng, vì vậy đây là kiểm tra của tôi: (((isset($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) == 'on')) || ((isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'))) không bao gồm kiểm tra cổng. Hãy thêm vào. :-)
Roland

@paolo đằng sau một proxy ngược SetEnvIf X-Forwarded-SSL on HTTPS=onsẽ thực hiện các mẹo. Nhưng điều này sẽ không hoạt động REQUEST_SCHEMEvì kết quả là php có vẻ tốt hơn để sử dụng$_SERVER['HTTPS']
Antony Gibbs

14

Điều này cũng hoạt động khi $_SERVER['HTTPS']không xác định

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

2
Chúng là một số máy chủ $_SERVER['HTTPS']chưa được xác định https được bật. Thế còn cái đó?
John Max

1
@JohnMax SERVER_PORTluôn được định nghĩa giải quyết vấn đề không xác định củaHTTPS
Thamaraiselvam

11

Tôi vừa gặp sự cố khi chạy máy chủ bằng mod_ssl của Apache, nhưng phpinfo () và var_dump ($ _SERVER) cho thấy PHP vẫn nghĩ tôi đang ở cổng 80.

Đây là cách giải quyết của tôi cho bất kỳ ai có cùng vấn đề ....

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

Dòng đáng chú ý là dòng SetEnv. Với vị trí này và sau khi khởi động lại, bạn sẽ có biến môi trường HTTPS mà bạn luôn mơ ước


5
Tốt hơn là chắc chắn HTTPS đang thực sự hoạt động; điều đó sẽ khiến máy chủ nói dối bạn nếu không.
Brad Koch

Ngoài ra, bạn cần mô-đun SetEnv để làm việc này. Nó được bật theo mặc định, nhưng bạn không bao giờ biết quản trị viên máy chủ có thể vô hiệu hóa cái gì.
toon81

Rất hữu ích trong trường hợp bạn đang ở trên docker qua proxy ngược. Cảm ơn!
dikirill

8

Tạo chức năng của riêng tôi từ việc đọc tất cả các bài viết trước:

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

Nếu bạn đang sử dụng Apache, bạn có thể luôn tin tưởng

$_SERVER["REQUEST_SCHEME"]

để xác minh sơ đồ của URL được yêu cầu. Nhưng, như đã đề cập trong các câu trả lời khác, nên thận trọng khi xác minh các tham số khác trước khi cho rằng SSL đang thực sự được sử dụng.


nó hoạt động trên XAMPP nhưng không phải trên centos / apache2 + PHP ... vì vậy nó không đáng tin cậy.
Firas Abd Alrahman

5

Câu trả lời THỰC SỰ: sẵn sàng để sao chép-dán vào tập lệnh [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_URIprotocolbây giờ là chính xác và sẵn sàng để được sử dụng; Ví dụ $site=$pv_URIprotocol.$_SERVER["SERVER_NAME"]. Đương nhiên, chuỗi có thể được thay thế bằng TRUE và FALSE. PV là viết tắt của PortalPress Var biến vì đây là bản sao chép trực tiếp sẽ luôn hoạt động. Phần này có thể được sử dụng trong một kịch bản sản xuất.


3

Tôi không nghĩ rằng thêm một cổng là ý tưởng hay - đặc biệt khi bạn có nhiều máy chủ với các bản dựng khác nhau. chỉ cần thêm một điều cần nhớ để thay đổi. nhìn vào doc tôi nghĩ rằng dòng kaisers cuối cùng khá tốt, vì vậy:

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

Có vẻ như hoàn toàn đủ.


3

Phương pháp đáng tin cậy duy nhất là phương pháp được mô tả bởi 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://");

Xem xét các vấn đề sau: Bạn đang sử dụng nginx với fastcgi, theo mặc định (debian, ubfox) fastgi_params chứa chỉ thị:

fastcgi_param HTTPS $ https;

nếu bạn KHÔNG sử dụng SSL, nó sẽ được dịch thành giá trị trống, không 'tắt', không phải 0 và bạn sẽ phải chịu số phận.

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


3

Tôi thấy những thông số này cũng được chấp nhận và nhiều khả năng không có kết quả dương tính giả khi chuyển đổi máy chủ web.

  1. $ _SERVER ['HTTPS_KEYSIZE']
  2. $ _SERVER ['HTTPS_SECRETKEYSIZE']
  3. $ _SERVER ['HTTPS_SERVER_ISSUER']
  4. $ _SERVER ['HTTPS_SERVER_SUB DỰ ÁN']

    if($_SERVER['HTTPS_KEYSIZE'] != NULL){/*do foobar*/}

Điều này không cho bạn biết bất cứ điều gì về việc sử dụng HTTPS với bộ cân bằng tải / proxy.
Brad

3

Cách ngắn nhất tôi đang sử dụng:

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

Nếu https được sử dụng, thì $ safe_connection là đúng.


echo (!empty($_SERVER['HTTPS'])?'https':'http');mang đến cho bạn httphoặchttps
Xavi Esteve

, làm cho nó(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie

2

Bạn có thể kiểm tra $_SERVER['SERVER_PORT']vì SSL thường chạy trên cổng 443, nhưng điều này không thể đánh lừa được.


$ _SERVER ['SERVER_PORT'] không có.
Tyler Carter

2

Bạn nghĩ gì về điều này?

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

Có, có. Nếu bạn chỉ dựa vào trống () PHP sẽ thoát khi có lỗi nếu không có chỉ mục 'HTTPS'.
toni rmc

3
"blank () về cơ bản là ngắn gọn tương đương với! isset ($ var) || $ var == false" - php.net/manual/en/feft.empty.php
John Magnolia

2
Bạn đúng rồi. Buồn cười tôi đã bỏ lỡ cái đó Tôi luôn nghĩ trống () sẽ thất bại nếu biến không tồn tại.
toni rmc

2

Trên biến máy chủ của tôi (Ubuntu 14.10, Apache 2.4, php 5.5) $_SERVER['HTTPS']không được đặt khi tập lệnh php được tải qua https. Tôi không biết điều gì là sai. Nhưng các dòng sau trong .htaccesstập tin khắc phục vấn đề này:

RewriteEngine on

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

1

Đây là một chức năng có thể sử dụng lại mà tôi đã sử dụng trong một thời gian. HTH.

Lưu ý: Giá trị của HTTPS_PORT (là hằng số tùy chỉnh trong mã của tôi) có thể khác nhau tùy theo điều kiện của bạn, ví dụ: nó có thể là 443 hoặc 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

chỉ để quan tâm, chrome hoàng yến tại thời điểm gửi

HTTPS : 1

đến máy chủ và tùy thuộc vào cách máy chủ được cấu hình có thể có nghĩa là bạn lấy lại được những điều sau

HTTPS : 1, on

Điều này đã phá vỡ ứng dụng của chúng tôi bởi vì chúng tôi đã thử nghiệm nếu trên, điều đó rõ ràng là không. Hiện tại, chỉ có chrome hoàng yến dường như làm điều này, nhưng đáng chú ý là những thứ từ chim hoàng yến thường rơi vào chrome "bình thường" một thời gian ngắn sau đó.


1

Nếu bạn sử dụng nginx làm hệ thống cân bằng tải, hãy kiểm tra $ _SERVER ['HTTP_HTTPS'] == 1 kiểm tra khác sẽ không thành công cho 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;

Mã đang kiểm tra bất cứ điều gì có thể và cũng hoạt động trên máy chủ web IIS. Chrome vì v44 không đặt tiêu đề HTTP: 1 nên việc kiểm tra HTTP_HTTPS là ổn. Nếu mã này không khớp với https, điều đó có nghĩa là máy chủ web hoặc máy chủ proxy của bạn được cấu hình kém. Bản thân Apache đặt cờ HTTPS chính xác nhưng có thể có vấn đề khi bạn sử dụng proxy (ví dụ nginx). Bạn phải đặt một số tiêu đề trong máy chủ ảo nginx https

proxy_set_header   X-HTTPS 1;

và sử dụng một số mô-đun Apache để đặt cờ HTTPS chính xác bằng cách tìm X-HTTPS từ proxy. Tìm kiếm mod_fakessl, mod_rpaf, v.v.


0

Nếu bạn đang sử dụng bộ cân bằng tải của Incapsula, bạn sẽ cần sử dụng IRule để tạo tiêu đề tùy chỉnh cho máy chủ của mình. Tôi đã tạo một tiêu đề HTTP_X_FORWARDED_PROTO bằng với "http" nếu cổng được đặt thành 80 và "https" nếu nó bằng 443.


0

Tôi sẽ thêm một bộ lọc toàn cầu để đảm bảo mọi thứ tôi đang kiểm tra là chính xác;

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

Tôi có cơ hội tiến thêm một bước và xác định xem trang web tôi đang kết nối có khả năng SSL không (một dự án hỏi người dùng về URL của họ và chúng tôi cần xác minh rằng họ đã cài đặt gói API của chúng tôi trên trang web http hoặc https).

Đây là chức năng tôi sử dụng - về cơ bản, chỉ cần gọi URL qua cURL để xem https có hoạt động không!

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

Đây là cách đáng tin cậy nhất mà tôi đã tìm thấy không chỉ tìm ra NẾU bạn đang sử dụng https (như câu hỏi yêu cầu), mà nếu bạn CÓ THỂ (hoặc thậm chí NÊN) đang sử dụng https.

LƯU Ý: có thể (mặc dù không thực sự có khả năng ...) rằng một trang web có thể có các trang http và https khác nhau (vì vậy nếu bạn được yêu cầu sử dụng http, có thể bạn không cần thay đổi ..) Phần lớn các trang web là như nhau và có lẽ nên tự định tuyến lại cho bạn, nhưng kiểm tra bổ sung này có công dụng của nó (chắc chắn như tôi đã nói, trong dự án nơi người dùng nhập thông tin trang web của họ và bạn muốn chắc chắn từ phía máy chủ)


0

Đây là cách tôi tìm cách giải quyết điều này

$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

Tôi đã sử dụng đề xuất chính ở đây và cảm thấy khó chịu với "Thông báo PHP" trong nhật ký khi HTTPS chưa được đặt. Bạn có thể tránh nó bằng cách sử dụng toán tử hợp nhất null "??":

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

(Lưu ý: không có sẵn trước php v7)


-7

Theo bài đăng của hobodave: "Đặt thành giá trị không trống nếu tập lệnh được truy vấn thông qua giao thức HTTPS."

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

làm cho nó(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
Tivie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.