HTTPS:最完整的PHP测试

时间:2017-12-12 01:49:34

标签: php apache iis nginx https

测试HTTPS

我已经看过许多快速简便的 PHP测试,用于检查请求是HTTPS还是HTTP。

但是,大多数 失败 以这种方式或那种方式。例如IIS,Nginx,Apache,其他Web服务器,负载平衡器,支持反向代理。

我正在寻找在我的工作组中使用的最佳实践,可移植代码。

你能否提出任何改进建议?

以下是我所拥有的(将PROTOCOL定义为'https://'或'http://')。

<!php
if ( !defined( 'PROTOCOL' ) ) {
    if ( !empty( $_SERVER[ 'HTTPS' ] ) && $_SERVER[ 'HTTPS' ] !== 'off' ) {
        // good in IIS and Apache
        if ( 'on' == strtolower( $_SERVER[ 'HTTPS' ] ) ) define( 'PROTOCOL', 'https://' );
        if ( '1' == $_SERVER[ 'HTTPS' ] ) define( 'PROTOCOL', 'https://' );
    } elseif ( !empty( $_SERVER[ 'HTTP_FORWARDED' ] ) && false !== strpos( strtolower( $_SERVER[ 'HTTP_FORWARDED' ] ), 'https' ) ) {
        // new standard RFC 7239 - reverse-proxies and load balancers.
        // not widely implimented yet
        define( 'PROTOCOL', 'https://' );
    } elseif ( !empty( $_SERVER[ 'SERVER_PORT' ] ) && ( '443' == $_SERVER[ 'SERVER_PORT' ] ) ) {
        // Is it port 443? Default Port for HTTPS
        define( 'PROTOCOL', 'https://' );
    } elseif ( !empty( $_SERVER[ 'HTTP_X_FORWARDED_SSL' ] ) && ( 'on' == strtolower( $_SERVER[ 'HTTP_X_FORWARDED_SSL' ] ) ) ) {
        // reverse-proxies and load balancers - nginx.
        define( 'PROTOCOL', 'https://' );
    } elseif ( !empty( $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] ) && ( 'https' == strtolower( $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] ) ) ) {
        // reverse-proxies and some load balancers.
        define( 'PROTOCOL', 'https://' );
    } elseif ( !empty( $_SERVER[ 'X_FORWARDED_PROTO' ] ) && ( 'https' == strtolower( $_SERVER[ 'X_FORWARDED_PROTO' ] ) ) ) {
        // reverse-proxies and some load balancers.
        define( 'PROTOCOL', 'https://' );
    } else {
        // default to http://
        define( 'PROTOCOL', 'http://' );
    }
}

我理解并接受一个人 可以(付出一些努力)使用没有SSL的端口443。尽管如此,端口443是默认的HTTPS端口。

WordPress做什么

对于讨论,这是WordPress使用的内容,但它有 许多失败 (负载均衡器,反向代理等)。

<!php
function is_ssl() {
    if ( isset( $_SERVER['HTTPS'] ) ) {
        if ( 'on' == strtolower( $_SERVER['HTTPS'] ) ) {
            return true;
        }

        if ( '1' == $_SERVER['HTTPS'] ) {
            return true;
        }
    } elseif ( isset($_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
        return true;
    }
    return false;
}

在官方WordPress Code Referance Page上,对于函数 is_ssl(),是这句话:

  

它不适用于某些负载均衡器背后的网站,尤其是网络解决方案托管网站。 ...有关详细信息,请阅读“WordPress is_ssl()在某些负载均衡器后面不起作用。”

     

支持HTTP_X_FORWARDED_PROTO的负载平衡器或反向代理背后的网站可以通过将以下代码添加到require_once调用上方的wp-config.php文件来修复:

<!php
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
  $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
    $_SERVER['HTTPS'] = 'on';

本页未提及:

  • $_SERVER[ 'HTTP_X_FORWARDED_SSL' ]
  • $_SERVER[ 'X_FORWARDED_PROTO' ]

但是,许多其他网页都提到了它们。

2 个答案:

答案 0 :(得分:0)

从PHP的角度来看,正确的检查方法应该是检查$ _SERVER [&#39; HTTPS&#39;]是非空值而不是字符串"off"。< / p>

if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
}

您的代码似乎要做的是要求此值为&#34; 1&#34;或&#34; on&#34;。但是,由于PHP手册暗示这可以是任何非空值(除了&#34; off&#34;),如果它是&#34; 1&#以外的非空值,则会失败34;或&#34; on&#34;。在实践中,这可能永远不会发生,但遵循规范只是为了确定是好的。

  • 可以使用SERVER_PORT,但如果HTTPS没有,那么没有理由可行。此外,某些服务器配置错误可能允许在端口443上使用普通HTTP。

  • HTTP_X_FORWARDED_SSL和HTTP_X_FORWARDED_PROTO不可靠,因为任何客户都可以设置这些(即伪造它们)。

  • X_FORWARDED_PROTO可能是HTTP_X_FORWARDED_PROTO的替代品,可能存在同样的问题。

如果您正在执行上述操作但仍无法检测到它,那么错误位于PHP之外,您的服务器层根本就不会将HTTPS状态传递给您。否则,没有100%可靠的方法来检测它,而且大多数尝试都是讨厌的黑客攻击。

特别是,如果您的服务器以普通HTTP的形式接收它,但您有一个负载均衡器/转发代理在上游进行SSL终止,那么您无法可靠地检测是否通过HTTPS,因为该信息不可用于本地服务器。现在,在这里我会问你为什么要知道请求是否来自HTTPS,因为适当的解决方案取决于你使用这些信息的内容。最可靠的解决方案就是要求HTTPS,阻止所有普通的HTTP,此时您可以在应用程序中采用HTTPS。否则,也许上游代理设置了您可以读取的特定HTTP标头,但是您必须确保没有通过代理就没有流量可以直接到达本地服务器,并且代理会删除传入请求中的任何此类标头,以便他们不能伪造。

答案 1 :(得分:0)

下面是一个有点改进的wordpress功能。我添加了一个条件来检查HTTP_X_FORWARDED_PROTO标头值。因此,这将适用于托管在负载均衡器/反向代理之后的大多数网站。

/**
 * Detect SSL status
 * @return boolean
 */
function is_ssl() {

    if ( isset($_SERVER['HTTPS']) ) {
        if ( 'on' == strtolower($_SERVER['HTTPS']) )
            return true;
        if ( '1' == $_SERVER['HTTPS'] )
            return true;
    } 
    elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
        return true;
    }
    elseif(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO']){
        return true;
    }

    return false;
}