【问题标题】:Secure and Flexible Cross-Domain Sessions安全灵活的跨域会话
【发布时间】:2012-12-10 18:13:50
【问题描述】:

我遇到了一个问题,希望您能提供帮助。假设我在一家名为“Blammo”的假设公司工作,我们有一个名为“Log”的假设产品。我正在尝试建立一个系统,有人可以登录到 logfromblammo.com 并订购我们的一些产品,然后当他们准备购买时,去 checkout.blammo.com 支付他们的订单。最终,我想让 Blammo 推出一个新的假设产品,它有自己的网站:rockfromblammo.com,并让该网站也能够与 checkout.blammo.com 共享会话,这样用户就可以在这两种产品中拥有一个购物车网站。

当然,上面描述的假设场景并不是我公司的实际运作方式,但它是我需要做的一个很好的例子。我们有一个现有的用户数据库,我们有办法在我们的任何站点上验证我们的任何用户,但我的目标是允许用户从一个站点无缝地跨越到另一个站点,而无需重新验证。这也将允许我们将购物车等数据无缝传输到结帐站点。

我(简要地)研究了 OpenID 等解决方案,但我需要能够将我们拥有的任何解决方案与我们现有的身份验证方法集成在一起,这种方法并不是非常强大。有没有什么好方法可以单独通过 PHP 做到这一点?

【问题讨论】:

  • 所以基本上你的网站有多个二级域名而不是product.site.com kind?
  • 我认为您最好的解决方案是让rockfromblammo.com 重定向到rockfrom.blammo.com——在这种情况下,安全性是一个大问题,并且可以访问两个不同域的购物车打开了一些第三方找出如何做同样事情的风险。
  • @Blazemonger 这不仅仅是一个安全风险,它不能跨域完成。只能跨子域!
  • @BenCarey 哦,我相信它可以完成。它只需要重写整个购物车逻辑并使系统完全不安全。

标签: php session cross-domain security


【解决方案1】:

您可以做的是在站点之间创建“交叉”链接以进行会话。

最简单的方法是通过查询字符串传递会话ID;例如

http://whateverblammo.com/?sessid=XXYYZZ

在您开始认为任何人都可以捕获该信息之前,请考虑一下您的 Cookie 是如何传输的;假设您没有使用 SSL,那么对于窃听网络的人来说并没有太大区别。

这并不意味着它是安全的;一方面,用户可能会不小心复制/粘贴地址栏,从而泄露他们的会话。为了限制这种暴露,您可以在收到后立即重定向到没有会话 id 的页面。

请注意,在会话 id 上使用 mcrypt() 并没有多大帮助,因为问题不是值的可见性;会话劫持不关心底层的价值,只关心 url 的可重现性。

您必须确保 id 只能使用一次;这可以通过创建一个跟踪使用计数的会话变量来完成:

$_SESSION['extids'] = array();

$ext = md5(uniqid(mt_rand(), true)); // just a semi random diddy
$_SESSION['extids'][$ext] = 1;

$link = 'http://othersite/?' . http_build_query('sessid' => session_id() . '-' . $ext);

收到时:

list($sid, $ext) = explode('-', $_GET['sessid']);
session_id($sid);
session_start();
if (isset($_SESSION['extids'][$ext])) {
    // okay, make sure it can't be used again
    unset($_SESSION['extids'][$ext]);
}

每次跨越边界时都需要这些链接,因为会话可能自上次以来已重新生成。

【讨论】:

  • 这是一个旧答案,但值得注意的是,如果用户在穿越域边界时碰巧共享他们的屏幕,则会出现潜在的安全问题。令牌将在 URL 中公开,然后可以用来劫持会话,如果它是长期存在的。
  • 答案中已经提到了潜在的安全问题?
  • 啊,关于它被使用过一次。没错,我错过了。尽管现在许多系统都使用无状态服务器,因此在不使用数据库或其他外部存储的情况下跟踪使用次数是很棘手的。
【解决方案2】:

在跨域 Ajax 中,您可能会发现 cookie 以及随后的会话因跨域请求而丢失。如果您要从站点 example.com 向子域 s2.example.com 进行 ajax 调用,则需要在 PHP 的标头中使用属性:

header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Credentials: true');

在JS中你要添加

xhrFields: { withCredentials: true }

否则 cookie 不会被传递,您将无法使用子域上的会话。

对子域的完整 JS 请求不会丢失会话

$.ajax({
    url: "https://s2.example.com/api.php?foo=1&bar=2",
    xhrFields: { withCredentials: true },
    success:function(e){
        jsn=$.parseJSON(e);
        if(jsn.status=="success") { 
                alert('OK!');
        } else {
                alert('Error!');
        }
    }
}) 

【讨论】:

    【解决方案3】:

    可以完成,但不是用简单的cookies,它不是微不足道的。您所追求的是单点登录 (SSO) 解决方案,类似于 Google 在 i.google.com、gmail.com、youtube.com 等上共享登录信息。

    我过去曾使用 OpenID 来实现这一点。

    基本思想是有一个单一的身份验证域(提供者),每当其中一个站点(消费者)想要对用户进行身份验证时,他们会将他们重定向到身份验证域。如果他们没有登录,他们可以使用您需要的任何详细信息登录。

    如果他们已经登录(即使从不同的目标站点),他们不需要再次登录。

    然后用户会被发送回目标站点,并在 url 中添加一个令牌。目标站点的服务器使用此令牌来验证用户是否已通过身份验证服务器进行身份验证。

    这是一个非常简单的解释。做到这一点并不难,安全地做到这一点更是如此。安全地生成和验证令牌的细节是具有挑战性的部分。这就是为什么我建议构建一个设计良好的系统,例如 OpenID。

    【讨论】:

      【解决方案4】:

      您需要像这样设置会话 cookie 域:

      session_set_cookie_params($lifetime,$path,'.site.com')
      

      这仅适用于站点位于相同域名(包括 TLD(顶级域))上的情况。

      更多信息请见here

      或者,如果您正在考虑尝试跨域访问会话,例如从 site1.netsite2.com,则无法这样做。

      【讨论】:

        【解决方案5】:

        如果您的网站可以依赖 Javascript 来运行,那么您大概可以执行以下操作:

        假设您在 blammo.com 上有一个会话,并且您想从 rockblammo.com 访问它。在rockblammo.com 页面上,您可以从blammo.com/get-session.js 加载<script>,这将(从服务器端)返回会话ID。一旦返回,您在页面中插入一个新的<script> 标记,指向rockblammo.com/set-session.js?sessionId=XXX,其中XXX 是您刚刚从blammo.com 获得的会话ID。现在,在rockblammo.com 的服务器端,会话cookie 被更新并设置为这个会话ID。展望未来,这两个页面现在将共享相同的 session-id,并且假设它们可以访问后端的相同会话存储,它们将保持同步。

        例如blammo.com/get-session.js 的输出将是:

        var sessionId = "XXX";
        var s = document.createElement("script");
        s.src = "/set-session.js?sessionId=" + escape(sessionId);
        document.body.appendChild(s);
        

        rockblammo.com/set-session.js 的输出将为空白,但会包含一个 http 标头,例如:

        Set-Cookie: sessionId=XXX
        

        如果您不想依赖 Javascript,您可以通过在两个站点之间来回重定向并在查询字符串参数 (GET 参数) 中传递 sessionId 来做同样的事情。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-29
          • 2010-11-23
          • 1970-01-01
          • 2011-01-23
          • 2010-10-25
          • 2016-11-04
          相关资源
          最近更新 更多