安全灵活的跨域会话

问题描述 投票:6回答:5

我有一个问题,希望你能帮忙。假设我为一家名为“Blammo”的假想公司工作,我们有一个名为“Log”的假设产品。我正在尝试建立一个系统,有人可以登录到logfromblammo.com并订购我们的一些产品,然后当他们准备购买时,请到checkout.blammo.com支付他们的订单。最终我想允许Blammo在其自己的网站上推出一个新的假设产品:rockfromblammo.com,并且该网站还能够与checkout.blammo.com共享会话,以便用户可以在两个产品上同时拥有一个购物车网站。

当然,上面描述的假设情景并不是我公司的实际运作方式,但它是我需要做的一个公平的例子。我们有一个现有的用户数据库,我们有办法对我们任何网站上的任何用户进行身份验证,但我的目标是允许用户无需重新进行身份验证即可从一个站点无缝跨越到另一个站点。这也允许我们将诸如购物车之类的数据无缝地传输到结账站点。

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

php session cross-domain security
5个回答
7
投票

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

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

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

在您开始认为任何人都可以捕获该信息之前,请考虑一下您的cookie如何转移;假设您没有使用SSL,那么点击网络的人没有太大区别。

这并不意味着它是安全的;例如,用户可能会意外地复制/粘贴地址栏,从而泄露其会话。要限制此曝光,您可以在收到后立即重定向到没有会话ID的页面。

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

你必须确保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]);
}

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


3
投票

它可以完成,但不是简单的cookie,它不是微不足道的。你所追求的是一个单点登录(SSO)解决方案,类似于谷歌的分享登录i.google.com,gmail.com,youtube.com等。

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

基本思想是拥有一个身份验证域(Provider),只要其中一个站点(Consumer)想要对用户进行身份验证,他们就会将它们重定向到身份验证域。如果他们没有登录,他们可以使用您需要的任何详细信息登录。

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

然后,通过在URL中添加令牌,将用户发送回目标站点。目标站点的服务器使用此令牌来验证用户是否使用身份验证服务器进行了身份验证。

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


1
投票

您需要设置会话cookie域,如下所示:

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

这仅在站点位于同一域名(包括TLD(顶级域))时才有效。

有关更多信息,请参阅here

或者,如果您正在尝试访问跨域的会话,例如从site1.netsite2.com,则无法完成。


1
投票

在跨域Ajax中,您可能会发现cookie和随后的会话因跨域请求而丢失。如果您要从您的站点example.com进行ajax调用到您的子域s2.example.com,您将需要在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!');
        }
    }
}) 

0
投票

如果你的网站可以依赖Javascript来运行,你可能会做如下的事情:

假设您在blammo.com上有一个会话,并且您想从rockblammo.com访问它。在rockblammo.com页面上,您可以从<script>加载blammo.com/get-session.js,这将(从服务器端)返回session-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,可以通过在两个站点之间重新定向并在query-string参数(GET param)中传递sessionId来完成相同的操作。

© www.soinside.com 2019 - 2024. All rights reserved.