安全灵活的跨域会话

时间:2012-12-10 18:13:50

标签: php session cross-domain security

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

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

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

5 个答案:

答案 0 :(得分:7)

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

最简单的方法是通过查询字符串传递会话ID; e.g。

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]);
}

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

答案 1 :(得分:3)

可以完成,但不能使用简单的Cookie,这不是一件容易的事。你所追求的是一个单点登录(SSO)解决方案,类似于谷歌的分享登录i.google.com,gmail.com,youtube.com等。

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

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

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

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

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

答案 2 :(得分:1)

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

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

这仅适用于网站位于同一域名的情况,包括TLD(顶级域名)。

有关详细信息,请参阅here

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

答案 3 :(得分:1)

在跨域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!');
        }
    }
}) 

答案 4 :(得分:0)

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

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

E.g。 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来实现相同的目的。