在 PHP 中从 HTTP 切换到 HTTPS 时会话丢失

Posted

技术标签:

【中文标题】在 PHP 中从 HTTP 切换到 HTTPS 时会话丢失【英文标题】:Session lost when switching from HTTP to HTTPS in PHP 【发布时间】:2010-10-01 06:53:57 【问题描述】:

将用户发送到结帐页面时,他们会从http://sitename.com 切换到https://sitename.com

因此,$_SESSION 变量丢失。

该网站有一个有效的 SSL 证书,它可能有用也可能没用。

【问题讨论】:

您使用的是哪个浏览器?是否涉及任何子域,例如“www.”?你能在别处重现这个问题吗? 【参考方案1】:

当您在同一服务器上的 HTTP 和 HTTPS 服务之间切换时,您的 HTTP 会话 ID 不会传递给 HTTPS 会话。您可以通过以下三种可能的方式之一将会话 ID 从 HTTP 页面传递到 HTTPS 页面来设置它:

来自php: session_start:

session_start() 创建会话或恢复当前会话 在通过请求传递的当前会话 id 上,例如 GET、POST 或 cookie

当您使用会话时,您通常会以session_start() 开始您的脚本。如果浏览器设置了会话 ID cookie,session_start() 将使用该会话 ID。如果浏览器没有设置会话 ID cookie,session_start() 将创建一个新的。

如果未设置会话 ID(在您的示例中,浏览器正在为 HTTPS 会话创建新的会话 ID cookie),您可以使用 session_id() 函数设置它。 session_id() 还方便地将会话 ID 作为字符串返回。所以

...

$currentSessionID = session_id();

...

$currentSessionID 变量设置为等于当前会话ID,并且

...

session_id($aSessionID);

...

将浏览器中的 sessionID cookie 设置为$aSessionID。来自PHP: session_id

这是一个包含两个脚本的示例。一种通过 HTTP 访问,另一种通过 HTTPS 访问。它们必须在同一台服务器上才能维护会话数据。

脚本 1(HTTP)

<?php

// This script will create a session and display a link to your secure server address
// to transfer your session ID. In this example, the secure page to receive the session
// ID is located at http://www.yoursite.com/safePages/securePage.php

// Start a session using the current session ID stored in a cookie, or create
// a new session if none is set.
session_start();

$currentSessionID = session_id();

// Set a variable that will be retrieved with the HTTPS script.
$_SESSION['testvariable'] = 'It worked';

// $secureServerDomain is the domain of your secure server
$secureServerDomain = 'www.yoursite.com';

// $securePagePath is the path to the page that will receive and set the session ID.
$securePagePath = '/safePages/securePage.php'

echo '<a href="https://' . $secureServerDomain . $securePagePath . '?session="' . $currentSessionID . '">Click here to transfer your session to the secure server</a>';

?>

脚本 2(HTTPS)

<?php

// Retrieve the session ID as passed via the GET method.
$currentSessionID = $_GET['session'];

// Set a cookie for the session ID.
session_id($currentSessionID);

// Start a session.
session_start();

// Test retrieval of variable set when using HTTP.
if (!empty($_SESSION['testvariable'])) 
      echo $_SESSION['testvariable'];
 else 
      echo 'It did not work.';


?>

为此,HTTP 和 HTTPS 服务器必须使用相同的会话数据存储基板(即,对于默认文件处理程序,在具有相同 php.ini 的同一物理机器上运行)。这里有一些安全漏洞,所以我不会使用这个代码来传输敏感信息。这只是一个可行的例子。

当我之前遇到这个问题时,我想出了上面的快速解决方法,但我只记得问题的最初原因。我从http://www.example.com/page.php 到https://example.com/page.php(注意缺少“www”)。确保http://www.example.com/page.php 将链接到https://www.example.com/page.php 并且http://example.com 将链接到https://example.com/page.php。

PS,我实际上并没有运行这些脚本,所以可能有一两个错字导致它们无法正常运行。

【讨论】:

另外使用加盐哈希可以确保此过程的安全。 我在这里看不到安全问题的性质 - 知道任何描述它们的资源吗? 安全问题:有人可能会不断地访问您的站点并猜测 session_id 变量,最终劫持其他人的会话。我看不出加盐哈希是如何工作的,因为这是一种单向操作;您已经对密码进行了加盐哈希处理,并对密码尝试执行相同的操作,但在这种情况下,这不太可行;你需要原始值。 @DeanJ - 您在服务器端生成校验和,并将其作为参数沿 URL 解析。例如,您可以生成像这样的哈希:$hash = md5($currentSessionID.'somesecretsalt'),然后 url 应该看起来像 ?session='.$currentSessionID.'&hash='.$hash。然后在目标站点上检查 md5($_GET['session'].'somesecretsalt') == $_GET['hash'].这样你就可以看出, session_id 不仅仅是虚构的。会话 id 必须匹配服务器生成的哈希(使用长盐值时几乎不可能破解)。 +1 注意到“从example.com/page.php 到example.com/page.php”的问题——这对我来说也是一个问题。我通过修改我的 .htaccess 文件以完全阻止 www 解决了这个问题: RewriteCond %HTTP_HOST ^www\.example\.com$ [NC] RewriteRule ^.*$ example.com%REQUEST_URI [R=301,L] 【参考方案2】:

听起来会话 cookie 设置为安全的。 Cookie 有一个 "secure" 标志,如果设置为 true,则意味着 cookie 不会发送到非 https 站点。 PHP 可能将其用于其会话 cookie。您可以使用 session_set_cookie_params 函数或 php.ini 中的 session.cookie_secure 设置来更改它。

【讨论】:

OP 指定在传输 加密连接时 cookie 丢失。 secure 标志不适用于此处。 @Unsigned - 如果您切换到安全页面并告诉该页面仅使用安全会话 cookie,它必须创建一个新的安全 cookie,并且不能使用现有的不安全 cookie。 @martinstoeckli - 是的。但是,默认设置为off。如果是on,cookie 就不会被发送到 HTTP 页面,这意味着 cookie 也不应该存在于那里。因此,cookie 不安全。 @JW 是对的,花点时间弄清楚,'session_start (); setcookie(session_name(), session_id(), NULL, NULL, NULL, 0);'为 http 和 https 设置一个 cookie,防止会话丢失。【参考方案3】:

我们也遇到过这个问题。原来是因为我们在 PHP 安装中使用了suhosin 补丁。我们通过在/etc/php.d/suhosin.ini 中设置suhosin.session.cryptdocroot = Off 来修复它。

有关suhosin.session.cryptdocroot 的suhosin 手册,请参阅http://www.hardened-php.net/suhosin/configuration.html#suhosin.session.cryptdocroot。

我们最初从这篇博文中找到了解决方法:http://www.yireo.com/blog/general-news/315-switch-between-http-and-https-looses-php-session。

【讨论】:

我还需要禁用suhosin.cookie.cryptdocroot【参考方案4】:

以下解决方案假定安全和非安全服务器可以访问相同的后端服务(缓存、数据库存储等)。

当用户完成购物后将其发送到我们的结帐流程时,我们必须处理同样的问题。为了解决这个问题,我们放置了一个缓存层并缓存了所有相关数据。例如,我们将从会话值中收集产品 ID 和用户 ID,将它们序列化,创建哈希,最后使用哈希作为键将会话数据存储在缓存中。然后,我们会将用户重定向到带有 url 中的哈希的安全站点。

当用户最终访问安全站点时,我们会尝试根据哈希值从缓存中提取数据。然后使用用户 ID 和产品 ID,我们可以从数据库中加载所有定价和描述数据,并呈现给用户以供最终结帐审查。

缓存数据易失性存在继承风险,但我们从未遇到过任何问题,因为重定向发生得很快。

【讨论】:

我们的解决方案相似但有一个不同。我建议存储在数据库中,而您暗示了缓存。他如何创建一个公共内存以在不同会话之间使用? @UğurGümüşhan:很好。与缓存(相对于流量)相比,数据库解决方案会更慢,但它是持久的。正如我在帖子中提到的,我们使用在发送端(非 ssl)生成的哈希,在接收端(ssl)查找数据。 这与在 URL 中发送会话 ID 有何不同(如其他答案中所述)?除了散列可能更长更难猜吗? @w3d:不确定你的意思。如果您愿意,可以使用会话 ID 或任何字符组合。关键是,您在一侧缓存会话数据,并使用查找键将用户重定向到另一侧,因此您可以使用旧会话的数据创建新会话。【参考方案5】:

看起来您的会话 cookie 是使用安全标志创建的,但是由于会话 cookie 没有被传递,您的结帐页面的 url 有一些东西。

或者可能,您的会话 cookie 不安全 - 只是结帐页面的 url 不同(http://mysite.com 与 http://www.mysite.com)浏览器没有发送 cookie。

如果您想了解更多关于从 http 切换到 https 以及反之亦然的信息 - 请查看 at my writeup on selective ssl :-)

【讨论】:

【参考方案6】:

您不能在不同域之间传递会话值。您必须使用 http post-get 或数据库来传递您的值。为了安全起见,您可以将所有值连接到一个字符串中并使用

sha1($string)

并将其与您的值一起发布并计算其他页面获取的值的 sha1,然后比较哈希值。

不同域上的 Post 方法会导致浏览器显示安全消息,所以不要使用它。

使用 url 获取方法是不安全的,您需要在重定向页面上要求输入密码才能允许系统中的获取参数。

如果您需要安全,请勿使用 cookie。

我建议的方式是,将值保存在数据库中并生成一个密钥,然后使用您的密钥创建重定向链接,使用具有密钥的 get 参数转发用户页面,然后页面用户被重定向到获取该密钥,获取数据并删除密钥。 您可以使用 sha1 生成密钥

PAGE 1---
$key=sha1($allvalsconcat);
//insert your session values to a database also the key in a column
header("Location: page2.php?key=".$key);

PAGE 2---
// select from database where key=$_GET["key"];
// delete from table where key=$key

这很安全。

可能发生的事情: 为参数“key”输入随机值以使您的网站将数据加载到内存中的脚本?

这不会发生,因为您在使用后删除了该条目。 一些常见的误解是 get 值是不安全的,应始终避免。

如果你想要完美的性能,你可以在mysql中将表引擎类型设置为“内存”。

【讨论】:

【参考方案7】:

我建议,除了这里大多数人所说的关于传输加密信息的内容外,还应该像通过第 3 方 API 传输敏感信息一样看待它。你怎么知道有人没有欺骗请求?有许多协议可以真正确认请求的真实性,具体取决于您的设置有多敏感。如果您不小心,您就会让自己的帐户遭到入侵。

即使它在同一台服务器上,请考虑:

当有人跟踪传递加密密钥的链接、表单操作等时,什么可以防止有人在访问您网站的安全版本之前对其进行嗅探?如果我在公共 WIFI 点,那也不会太牵强。我可以假装是您的站点,将请求重新路由到我自己的笔记本电脑,获取令牌,然后将访问者重定向回他们来的地方。他们会认为这是一个小故障,并且不知道。现在我可以以他们的身份登录,可能会用他们的信用卡购买价值 10,000 美元的东西,然后将其运送到其他地方。您在此处采取的谨慎程度应与敏感程度相匹配。

另外,请确保您的令牌过期(仅使用一次,在 X 秒后等),但我也会考虑在两端使用 Post-Redirect-Get 模式,即:

不要在页面上或以不安全站点的形式显示直接链接,而是显示一个链接,该链接随后将在后端重定向(并处理所有令牌/加密内容)。当您到达安全版本时,请执行相同操作(不要将“?token=asdfjalksfjla”参数留在 URL 中;重定向它)。

因此,正式的基于令牌的系统旨在解决这个问题,但仅仅为此实施 OAuth 可能是矫枉过正。在执行之前花一些时间计划潜在的漏洞。仅仅因为猜测令牌真的很难并不意味着它不可能(或者不可能发生冲突等),所以要相应地计划。

您可能还需要一个比 PHP 的内置处理程序更复杂的会话管理系统。我不知道你是否可以强制 PHP 在多次访问中继续一个会话(切换协议就是这样处理的)。

【讨论】:

【参考方案8】:

考虑对所有页面使用 HTTPS,这是避免此问题的最简单方法,并且会提高您网站的安全性。

如果您无法选择所有页面的 SSL,那么您可以使用以下方法: Switching between HTTP and HTTPS pages with secure session-cookie。背后的想法是,您让会话 cookie 不安全(因此可用于 HTTP 和 HTTPS 页面),但有第二个安全 cookie 来处理身份验证。这是区分“维护会话”和“身份验证”这两个关注点的好方法。

【讨论】:

【参考方案9】:

您可以管理 HTTP 到 HTTPS 或 HTTPS 到 HTTP 之间的会话:

    使用 GET 在页面之间传输会话 ID

    POST 会话 ID

    使用文件保存会话

    使用 Cookie 进行会话

    使用数据库保存会话

以下示例可用于使用 GET 进行传输...。

文件:http.php ………………

<?php

session_start();

$sessionID = session_id();

$_SESSION['demo'] = ‘Demo session between HTTP HTTPS’;

echo ‘<a href=”https://www.svnlabs.com/https.php?session=’.$sessionID.’”>Demo session from HTTP to HTTPS</a>’;

?>

文件:https.php ………………

<?php

$sessionID = $_GET['session'];

session_id($sessionID);

session_start();

if (!empty($_SESSION['demo'])) 
echo $_SESSION['svnlabs'];
 else 
echo ‘Demo session failed’;


?>

IE7:此页面包含安全和非安全项目

您必须对页面上的所有静态资源(如 css、js、图像、flash 等)使用相对路径,以避免 IE 消息安全和非安全项目...

IE消息

【讨论】:

在 URL 中传递会话 ID 将使您容易受到各种攻击。仅使用 cookie 传输会话 id 的原因是,您可以强制仅对 cookie 的发送进行加密。【参考方案10】:

这可能是不可能的,因为 cookie 似乎正在丢失。您使用的浏览器必须认为它适用于完全不同的域。

你具体用的是什么浏览器?

【讨论】:

【参考方案11】:

默认情况下,我希望浏览器将与 http 和 https 的连接视为完全不同的会话。尽管约定是 http://someUrl/ 和 https://someUrl/ 将指向同一页面,但不能保证。您可以在端口 80 (http) 和端口 443 (https) 上运行完全不同的站点。

我不了解 PHP,但通常我不希望会话变量可以在安全会话和非安全会话之间免费使用,例如我不希望我上次结帐时的信用卡号可用于我随后访问的所有不安全页面。

请原谅非权威的答案,但我想我会放弃我的 2c,因为答案不多。

【讨论】:

我根本没想到会这样。问题可能是由于路径/安全标志的掩蔽问题。 “虽然惯例是……” - WTF?不,这是一种真正危险的做法。 @symcbean 两个 URL 都指向同一个页面并不意味着它们都可用。当然,“安全”页面只能通过 https 获得。您看不到的是 http//someUrl 和 https//someUrl 指向完全不同的内容,但这是完全可能的。银行不会在安全服务器上提供任何 http,但许多网站会将 80 和 443 绑定到同一个服务器,就像提问者一样。【参考方案12】:

您有专用 IP 吗? 在某些共享环境中,https 和 http 通过不同的服务器进行路由,因此切换实际上会失去对 cookie 的访问权限,因为它们位于不同的域中。

解决方案是: 专用ip

始终在所有页面上强制 https

【讨论】:

【参考方案13】:

我有类似的问题,但是,这个解决方案对我有好处,也许将来会帮助其他人

在你的 php.ini 中添加这个

suhosin.session.cryptdocroot = 关闭

suhosin.cookie.cryptdocroot = 关闭

【讨论】:

【参考方案14】:

我有一个解决方案..试试吧。

$_SESSION['test'] = 'test';
session_regenerate_id(true);

header("Location: /");// the header must be sent before session close
session_write_close(); // here you could also use exit();

【讨论】:

【参考方案15】:

我遇到了从 http 迁移到 https 的问题,我能够解决这个问题,将网站 URL 从 http://example.com 更改为 https://www.example.com

将 www 添加到 URL 为我解决了这个问题。

谢谢。

【讨论】:

【参考方案16】:

不要担心这是正常行为,因为 HTTPS 旨在确保安全,它正在发挥作用。

以下是一些技巧,您可以通过这些技巧在从 HTTP 切换到 HTTPS 时保持会话。

    使用 GET 在页面之间传输会话 ID

    POST 会话 ID

    使用文件保存会话

    使用 Cookie 进行会话

    使用数据库保存会话

希望你能通过我的回复有所收获。

【讨论】:

以上是关于在 PHP 中从 HTTP 切换到 HTTPS 时会话丢失的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PHP CURL 中从 POST 切换到 GET

在带有自定义域的 OpenShift 中从 HTTPS 重定向到 HTTP

如何在单击按钮时在android中从3G切换到2G

在 CloudFront 中从一个源同时设置 https 和 http

在 Azure CDN 标准 Verizon 中从 HTTP 重定向到 HTTPS

如何在本机反应中从相机按钮切换到视频录制按钮