使用 PHP 检查页面是不是被 SSL 访问

Posted

技术标签:

【中文标题】使用 PHP 检查页面是不是被 SSL 访问【英文标题】:Use PHP to check if page was accessed with SSL使用 PHP 检查页面是否被 SSL 访问 【发布时间】:2011-07-03 06:42:52 【问题描述】:

有没有办法检查当前页面是否使用 SSL 打开?例如,我希望我的登录页面 (login.php) 检查是否使用 SSL (https://mywebserver.com/login.php) 访问它。如果没有,请将它们重定向到页面的 SSL 版本。

差不多,我想确保用户安全地使用该页面。

【问题讨论】:

How can I prevent access to PHP files if the caller isn't using HTTPS? 的可能重复项 How To Find Out If You are Using HTTPS Without $_SERVER['HTTPS']的可能重复 【参考方案1】:

您应该能够检查$_SERVER['HTTPS'] 是否已设置,例如:

if (empty($_SERVER['HTTPS'])) 
    header('Location: https://mywebserver.com/login.php');
    exit;

【讨论】:

在 $_SERVER['HTTPS'] 总是“开启”或“关闭”的 IIS 上除外 怎么能做到动态,因为上面的url是静态的?【参考方案2】:
<?php
if ( !empty( $_SERVER['HTTPS'] ) ) 
  //do secure stuff
else
  //warn or redirect or whatever

?>

http://php.net/manual/en/reserved.variables.server.php

【讨论】:

感谢您的链接。不知道我是怎么错过那个页面的。 这是错误的方式,不是吗?如果我在 https 上检查 !empty,我会进入 else 部分,反之亦然。 @FooBar,在 HTTPS 上,它应该(完全依赖于 SAPI)让你进入 if 部分,而不是 else 部分。 这在 IIS 上失败,其中“HTTPS”在非安全连接上包含“关闭”。【参考方案3】:

如果您使用转发协议,您会发现这可能不起作用。例如,亚马逊的 ELB 可以处理 SSL 协商并通过端口 80 与您的应用服务器交互。

这个块处理:

    public function isSSL()
    
        if( !empty( $_SERVER['https'] ) )
            return true;

        if( !empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )
            return true;

        return false;
    

【讨论】:

IIS 在没有 SSL 时返回 $_SERVER['HTTPS'] = "off",因此您必须使用 if (!empty( $_SERVER['HTTPS'] &amp;&amp; $_SERVER['HTTPS'] != 'off') 来确保它在 IIS 上工作;) 这也可能是使用的值:$_SERVER['HTTP_X_FORWARDED_PORT'] == '443' @Wh1T3h4Ck5,这是他们需要修复的错误。不是我们的代码。 @Pacrerier 如果您支持 IIS,这是一个 需要修复的错误。或者不,如果你不这样做。问题中没有任何内容表明 IIS 不受支持,因此如果没有 Wh1T3h4Ck5 的修改,这不是一个完整的答案,因为它在某些情况下会给出错误的结果。【参考方案4】:

只是补充一点,如果是 nginx,检查 https 的方法是:

if (isset($_SERVER['SERVER_PORT']) &&
        ($_SERVER['SERVER_PORT'] === '443')) 
    return 'https';

【讨论】:

虽然这适用于许多情况,但如果您的 HTTPS 不在 443 上,这将失败... === '443' 看起来不对 - 它也在检查类型匹配,但为整数提供字符串值。 @QStudio === '443' 在这种情况下实际上是正确的,如果你评估 gettype($_SERVER['SERVER_PORT']) == 'string' @My1 如果您的 SSL 连接在到达其最终目的地之前在任何地方终止(即反向代理、负载平衡器,甚至 CloudFlare's Flexible SSL 选项),此解决方案也会“失败”。然而,就其本身而言,它实际上并没有失败,甚至可以用于进行完整性检查以验证端到端的“完整 SSL”连接。【参考方案5】:

小心。在我的 IIS 服务器上,$_SERVER['HTTPS'] 不为空,但值为 'off'。

所以我不得不这样做

if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on') 
    // no SSL request

【讨论】:

这可能是最好的做法,最好也添加严格检查...$_SERVER['HTTPS'] !== 'on'【参考方案6】:

另一种方法是检查 HTTPS cookie 是否存在。首先,您的服务器需要向浏览器发送带有 secure 标志的 cookie:

Set-Cookie:some_key=some_value;secure

在您的服务器向浏览器发送 cookie 后,每当浏览器向您的服务器请求页面时,它都会发送安全 cookie some_key=some_value仅当它正在请求 HTTPS 页面时。这意味着如果您看到 cookie some_key=some_value 的存在,您就知道浏览器正在请求一个 HTTPS 页面。瞧!

浏览器支持非常好,因为这是安全的基础。当用户从非 HSTSed 域请求页面时,不支持 HTTPS cookie 的浏览器是 Firesheepable。

有关详细信息,请参阅:

https://httpsnow.org/help/securecookies

https://www.owasp.org/index.php/SecureFlag#Overview

https://***.com/a/13730187/632951

https://security.stackexchange.com/q/100/2379

【讨论】:

这不能在客户端进行欺骗,即通过手动将cookie添加到非安全页面中吗?【参考方案7】:

好吧,这是另一段代码。该代码将返回带有 https/http 的完整 url。

<?php

/**
 * Check whether URL is HTTPS/HTTP
 * @return boolean [description]
 */
function isSecure()


    if (
        ( ! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
        || ( ! empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
        || ( ! empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on')
        || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)
        || (isset($_SERVER['HTTP_X_FORWARDED_PORT']) && $_SERVER['HTTP_X_FORWARDED_PORT'] == 443)
        || (isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] == 'https')
    ) 
        return true;
     else 
        return false;
    


/**
 * Example Use
 */
define('APP_URL', (isSecure() ? 'https' : 'http') . "://$_SERVER['SERVER_NAME']".str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']));
echo APP_URL;


/**
 * +++++++++++++++++++++++++
 * OR - One line Code
 * +++++++++++++++++++++++++
 */
define('APP_URL', ((( ! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ( ! empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') || ( ! empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) || (isset($_SERVER['HTTP_X_FORWARDED_PORT']) && $_SERVER['HTTP_X_FORWARDED_PORT'] == 443) || (isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] == 'https') ) ? 'https' : 'http') . "://$_SERVER['SERVER_NAME']".str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']));
echo APP_URL;

?>

【讨论】:

【参考方案8】:

要使用 PHP 检查页面是否在没有 SSL 的情况下访问,您可以检查端口号。

// Most encrypted web sites use port 443
if ($_SERVER['SERVER_PORT']==443) 
    // Tell browser to always use HTTPS
    header('strict-transport-security: max-age=126230400');

elseif (isset($_SERVER['SERVER_PORT'])) 
    // Redirect current page to https with 301 Moved Permanently response
    header('location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301);
    exit;

这假设您的服务器配置了 SERVER_PORT 环境变量,并且您的网站的加密版本托管在端口 443 上。它还假设您的服务器不在负载平衡器后面。如果您的服务器位于负载均衡器后面,您可能需要更高级的解决方案,例如不依赖自定义 HTTP 标头的解决方案,该标头可能因负载均衡器而异:

// Set secure cookie to detect HTTPS as cookie will not exist otherwise.
header('set-cookie: __Secure-https=1; expires='.substr(gmdate('r', ($_SERVER['REQUEST_TIME']?: time())+126230400), 0, -5).'GMT; path=/; secure', false);
// Tell browser to always use HTTPS
header('strict-transport-security: max-age=126230400');
if (!isset($_COOKIE['__Secure-https']) && !isset($_GET['https'])) 
    // Redirect to secure version of site and add https=1 GET variable in case cookies are blocked
    header('location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].(strpos($_SERVER['REQUEST_URI'], '?')===false? '?': '&').'https=1', true, 307);
    exit;

如果上述解决方案存在问题,因为它会将 ?https=1 添加到您的 URL,那么您始终可以使用 javascript。在

之后将其添加到页面顶部:
<script>
// This will redirect all requests from http to https
if (location.protocol=='http:') 
    location.replace('https://'+location.host+location.pathname+location.search)
    document.write('<noscript>');// hack to stop page from displaying

</script>

如果您希望浏览器在访问您的网站时记住始终使用 HTTPS,请将以下内容添加到您的 PHP 脚本中:

header('strict-transport-security: max-age=126230400');

或者如果您希望浏览器预先加载您的偏好,请使用:

header('strict-transport-security: max-age=126230400; preload');// HTTPS will always be used!

如果您使用预加载功能,您需要提交您的网站以包含在 Chrome 的 HSTS 预加载列表中,以便浏览器预加载您的网站首选项。如果您使用预加载,建议您在没有 www 的裸域上托管您的网站。这是因为大多数人通常更容易在没有 www 的情况下输入您的域,并且通过预加载您的网站加载而不需要繁琐的重定向,因为 https 已经是默认设置。

【讨论】:

【参考方案9】:

检测服务器端 SLL 并添加一些内容:

function detectSSL(): ?bool 

    // check HTTPS protocol
    if( isset($_SERVER['HTTPS']) ) 

        if( 'off' !== strtolower($_SERVER['HTTPS']) ) 

            return true;

        

        if( 1 === (int)$_SERVER['HTTPS'] ) 

            return true;

        

    

    if( isset($_SERVER['HTTP_X_FORWARDED_SSL']) ) 

        if( 'on' === $_SERVER['HTTP_X_FORWARDED_SSL'] ) 

            return true;

        

    

    if( isset($_SERVER['HTTP_X_FORWARDED_PORT']) ) 

        if( 443 === (int)$_SERVER['HTTP_X_FORWARDED_PORT'] ) 

            return true;

        

    

    if( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) ) 

        if( strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https' ) 

            return true;

        

    

    if( isset($_SERVER['REQUEST_SCHEME']) ) 

        if( strtolower($_SERVER['REQUEST_SCHEME'] === 'https') ) 

            return true;

        

    

    // check server port
    if( isset($_SERVER['SERVER_PORT']) ) 

        if( 443 === (int)$_SERVER['SERVER_PORT'] ) 

            return true;

        

    

    // non-SSL
    return null;



// Set URI prefix
define('uri_prefix', detectSSL() ? 'https://' : 'http://');

define('site_host', strtolower(uri_prefix . $_SERVER['HTTP_HOST']));

.htaccess 的补充:

# SSL schema off
RewriteCond %HTTPS off

RewriteRule .* - [E=REQUEST_SCHEME:http]

# SSL schema
RewriteCond %HTTPS on

RewriteRule .* - [E=REQUEST_SCHEME:https]

【讨论】:

以上是关于使用 PHP 检查页面是不是被 SSL 访问的主要内容,如果未能解决你的问题,请参考以下文章

使用 php 链接到另一个 html 页面时访问被拒绝

检查是不是通过嵌入到 HTML 页面来加载 SWF(直接访问)?

检测页面是不是被机器人访问

如何使用 PHP 检查页面 url 中是不是包含 www?

centos下,nginx服务器,我用https访问php页面为啥总提示下载。http可以访问php页面

带有帐户激活检查的 PHP 登录页面 [重复]