使用 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'] && $_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 访问的主要内容,如果未能解决你的问题,请参考以下文章
检查是不是通过嵌入到 HTML 页面来加载 SWF(直接访问)?