跨域 Cookie

Posted

技术标签:

【中文标题】跨域 Cookie【英文标题】:Cross-Domain Cookies 【发布时间】:2011-03-21 12:08:42 【问题描述】:

我在两个不同的域中有两个 webapps WebApp1 和 WebApp2。

    我在 WebApp1 的 HttpResponse 中设置了一个 cookie。 如何从WebApp2中的HttpRequest读取相同的cookie?

我知道这听起来很奇怪,因为 cookie 是特定于给定域的,我们无法从不同的域访问它们;然而,我听说过可以在多个 web 应用程序之间共享的 CROSS-DOMAIN cookie。如何使用 CROSS-DOMAIN cookie 实现此要求?

注意:我正在尝试使用 J2EE webapps

【问题讨论】:

【参考方案1】:

是的,绝对有可能通过 domain2.com 从 domain1.com 获取 cookie。我的社交网络的一个社交插件也遇到了同样的问题,经过一天的研究,我找到了解决方案。

首先,在服务器端,您需要有以下标头:

header("Access-Control-Allow-Origin: http://origin.domain:port");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Methods: GET, POST");
header("Access-Control-Allow-Headers: Content-Type, *");

php 文件中,您可以使用 $_COOKIE[name]

其次,在客户端:

在您的 ajax 请求中,您需要包含 2 个参数

crossDomain: true
xhrFields:  withCredentials: true 

例子:

type: "get",
url: link,
crossDomain: true,
dataType: 'json',
xhrFields: 
  withCredentials: true

【讨论】:

或者,如果您不想过滤来源,只需使用 $_SERVER['HTTP_ORIGIN'] 而不是 * 这是唯一对我有用的东西。此外,* 未被接受为来源,因此需要 @Joel Teply 的提示。 如果第三方 cookie 被禁用(在某些浏览器情况下是自动的),这将不起作用。请参阅blog.zok.pw/web/2015/10/21/3rd-party-cookies-in-practice 和allannienhuis.com/archives/2013/11/03/… 了解更多信息。 不要使用 Joel 的提示,因为它“本质上”与将其设置为“*”相同,这可能会打开微妙的安全漏洞,因此不鼓励使用,请参阅 ***.com/questions/12001269/… 在哪个域的服务器端?【参考方案2】:

正如其他人所说,您不能共享 cookie,但您可以这样做:

    将所有 cookie 集中在一个域中,比如 cookiemaker.com 当用户向 example.com 发出请求时,您将他重定向到 cookiemaker.com cookiemaker.com 将他重定向回 example.com 并提供您需要的信息

当然,它并不完全安全,您必须在应用之间创建某种内部协议才能做到这一点。

最后,如果您在每个请求中都执行类似的操作,那么用户会非常烦人,但如果只是第一个请求则不会。

但我认为没有别的办法……

【讨论】:

如果没有其他办法,那么 StackExchange/OpenID 是如何工作的呢? @Hawken StackExchange/OpenID 遵循与上述相同的过程。您将被定向到不同的站点 (SO > SX),确认您的身份,然后将您需要的信息定向回 SO。 The OpenID Spec 解释更多,尽管Wikipedia does it more plainly。 实际上所有用户都登录了cookiemaker.com。并且它使用特殊且安全的消息将用户重定向到不同的站点,以验证他们是否已登录以及他们是谁。如何实现它取决于您,有无数种方法可以实现。也许你可以使用这个:jwt.io @Andrew_1510 cookiebaker 会更好;-) @alcuadrado,你能否解释得更详细一点:1)cookiemaker如何知道在用户通过身份验证后将用户重定向到哪里? origin-url 应该出现在查询字符串中吗? 2) 在第 3 步重定向到 example.com 时,如何将“您需要的信息”传递给它?【参考方案3】:

据我所知,cookie 受到“同源”政策的限制。但是,使用 CORS,您可以接收和使用“服务器 B”cookie 来在“服务器 B”上从“服务器 A”建立持久会话。

不过,这需要“服务器 B”上的一些标头:

Access-Control-Allow-Origin: http://server-a.domain.com
Access-Control-Allow-Credentials: true

您需要在所有“服务器 A”请求上发送标志“withCredentials”(例如:xhr.withCredentials = true;

你可以在这里阅读:

http://www.html5rocks.com/en/tutorials/cors/

https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS

【讨论】:

这对某些用户不起作用,因为如果禁用第三方 cookie,例如 CORS cookie 将不起作用。 Safari by default 例如Mozilla settings。 Google more examples 和 article about why Facebook 不使用第三方 cookie。 堆栈交换/openID 是否使用 CORS? FWIW 我刚刚用Credentials XHR 测试了一个普通的CORS,它可以在FF/Safari/Chrome 上运行......虽然我不怀疑facebook/google 使用更复杂的方案【参考方案4】:

没有跨域 cookie 这样的东西。您可以在 foo.example.combar.example.com 之间共享 cookie,但不能在 example.comexample2.com 之间共享 cookie,这是出于安全原因。

【讨论】:

您好,感谢您的回复,您能否在配置部分添加更清晰的内容,如何在 j2ee 环境中创建/配置域和子域??? 这是一个更适合serverfault.com 的问题,您将从该领域的专家那里得到答案。 嗨,我尝试有两个 webapps WebApp.domain.com ==> 在这里我在响应中添加 cookie 如下: Cookie cookie = new Cookie("namedCookie","test"); cookie.setDomain(".domain.com"); response.addCookie(cookie); WebApp1.domain.com==>这里我尝试访问cookie如下,但无法访问Cookie[]cks = request.getCookies(); for(int i=0;i 经常重复但不正确,请参阅下面或此处的答案***.com/questions/16186645/… 如何在foo.example.combar.example.com之间共享cookie?【参考方案5】:

最聪明的解决方案是在这方面遵循 facebook 的路径。当您访问任何域时,facebook 如何知道您是谁?其实很simple:

“赞”按钮实际上允许 Facebook 跟踪外部网站的所有访问者,无论他们是否点击它。 Facebook 可以这样做,因为他们使用 iframe 来显示按钮。 iframe 类似于页面中的嵌入式浏览器窗口。对按钮使用 iframe 和简单图像之间的区别在于 iframe 包含一个完整的网页——来自 Facebook。除了按钮和关于有多少人喜欢当前页面的信息之外,此页面上没有太多内容。

因此,当您在 cnn.com 上看到一个“赞”按钮时,您实际上是在同时访问 Facebook 页面。这允许 Facebook 读取您计算机上的 cookie,该 cookie 在您上次登录 Facebook 时创建。

每个浏览器的基本安全规则是,只有创建了 cookie 的网站才能在以后读取它。这就是 iframe 的优势:即使您访问不同的网站,它也允许 Facebook 读取您的 Facebook-cookie。这就是他们在 cnn.com 上认出你并在那里展示你的朋友的方式。

来源:

http://dorianroy.com/blog/2010/04/how-facebooks-like-button-works/ https://***.com/a/8256920/715483

【讨论】:

我认为 iframe 很少会被归类为做任何事情的最佳或最聪明的方式。但它是最简单的。 facebook的cookie不会被识别为第三方cookie吗?! Facebook域将无法访问父窗口cookie,如果他们愿意,最多可以得到window.parent.location,但这并不重要,因为父窗口有意识地选择添加facebook -iframe,这意味着在加载时 facebook 已经知道父页面标识符。基本上,父页面需要 facebook track-ability 功能。在用户方面,要阻止它,需要一些浏览器扩展来阻止不需要的域,例如 Chrome 的 uBlock origin。【参考方案6】:

跨域 cookie 不允许(即站点 A 无法在站点 B 上设置 cookie)。

但是一旦站点 A 设置了 cookie,您甚至可以在 从站点 B 到站点 A 的请求中发送该 cookie(i.e. cross-domain requests):

来自不同域的XMLHttpRequest无法为自己的域设置 cookie 值,除非在发出请求之前将 withCredentials 设置为 true。通过将withCredentials 设置为true 获得的第三方cookie仍将遵循同源策略,因此无法被请求脚本通过document.cookie 或从响应头。

确保做这些事情:

    在响应中设置 cookie 时 如果请求是跨站点的,则Set-Cookie 响应标头包括SameSite=None(注意从www.web.devstatic.web.dev 的请求实际上是一个同一站点 em> 请求,可以使用SameSite=Strict) 如果通过 HTTPS 提供,Set-Cookie 响应标头 应包含 Secure 属性;如看到here 和here 发送/接收 cookie 时: 请求是使用withCredentials: true 发出的,正如其他答案here 和here 中所述,包括原始请求,其响应设置首先设置cookie 对于fetch API,此属性为credentials: 'include',与withCredentials: true 对于jQuery's ajax method,请注意您可能需要提供参数crossDomain: true 服务器响应包括cross-origin headers,如Access-Control-Allow-OriginAccess-Control-Allow-CredentialsAccess-Control-Allow-HeadersAccess-Control-Allow-Methods 一般来说: 您的浏览器尚未禁用第 3 方 cookie。*

不需要的东西(只使用上面的):

    Set-Cookie 中的domain 属性;你可以选择一个根域(即a.example.com可以设置一个域值为example.com的cookie,但这不是必须的;cookie仍然会被发送到a.example.com,即使是从b.other-site.com发送的 为了让 cookie 在 Chrome 开发工具中可见,“应用程序”选项卡;如果 cookie HttpOnly 属性的值为 true,Chrome 不会在应用程序选项卡中显示 cookie 值(它应该在 时显示 cookie 值在初始请求中设置,并在后续响应中发送 withCredentials: true)

让我们澄清一下“域”与“站点”;快速提醒“解析 URL”对我有帮助。在这个网址https://example.com:8888/examples/index.html,记住这些主要部分(来自this paper):

“协议”:https:// “主机名/主机”:example.com “端口”:8888 “路径”:/examples/index.html

请注意“路径”和“站点”对于 Cookie 目的的区别。 “路径”与安全相关; “站点”与安全相关:

路径

服务器可以在Set-Cookie 中设置Path 属性,但似乎与安全无关:

请注意,path 旨在提高性能,而非安全性。即使路径不匹配,同源网页仍然可以通过document.cookie访问cookie。

网站

SameSite 属性,根据web.dev article,可以限制或允许跨站cookies;但什么是“网站”?

在这里准确理解“站点”的含义会很有帮助。该站点是域后缀和它之前的域部分的组合。例如,www.web.dev 域是 web.dev 站点的一部分...

这意味着从www.web.devstatic.web.dev 发出的请求是一个sameSite 请求。

public suffix list 定义了这个,所以 它不仅包括 .com 等***域名,还包括服务 喜欢github.io

这意味着从 my-project.github.ioyour-project.github.io 的请求是一个跨站点请求。

这表示公共后缀的左边;是子域(但subdomain is a part of the host; see the BONUS reply in this answer)

wwwwww.web.dev 中的子域;与static.web.dev 相同的站点 your-projectyour-project.github.io 中的域;单独的站点为my-project.github.io

在此 URL https://www.example.com:8888/examples/index.html 中,请记住以下部分:

“协议”:https:// “主机名”又名“主机”:example.com (在像“en.wikipedia.org”这样的情况下,整个“en.example.com”也是一个主机名) “端口”:8888 “网站”:example.com “域”:example.com “子域”:www “路径”:/examples/index.html

有用的链接:

https://web.dev/samesite-cookies-explained/ https://jisajournal.springeropen.com/articles/10.1186/1869-0238-4-13 https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03 https://inst.eecs.berkeley.edu/~cs261/fa17/scribe/web-security-1.pdf https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie

(小心;我正在 Chrome 隐身标签中测试我的功能;根据我的chrome://settings/cookies;我的设置是“在隐身模式中阻止第三方 cookie”,所以我无法测试 Cross - 隐身模式中的站点 cookie。)

【讨论】:

这不是跨域 cookie。没有什么比跨域cookies更好的了。这是您无法避免的严格规则。页面 A 永远不能为页面 B 设置 cookie。您只是发送页面 B 为来自页面 A 的页面 B 的请求创建的 Cookie。 好的,谢谢@Sebi2020 我更新了答案,希望我能抓住差异 这个答案清晰、透彻且很有帮助。请注意,如果您想使用凭证选项(withCredentials 用于 XMLHttpRequest,或credentials 用于 Fetch API),“Access-Control-Allow-Origin”不能是通配符 (*) 值。 MDN explanation。在服务器上,您必须从传入请求中获取来源,并在“Access-Control-Allow-Origin”标头中返回该值。这在安全方面是个好主意还是坏主意,我不知道。【参考方案7】:

您不能跨域共享 cookie。但是,您可以允许所有子域访问。要允许example.com 的所有子域具有访问权限,请将域设置为.example.com

但不能让 otherexample.com 访问 example.com 的 cookie。

【讨论】:

那么.google.com cookie 在浏览 YouTube 时怎么会出现? 谷歌分析标签。这些 cookie 来自 google.com,而不是来自 youtube.com。【参考方案8】:

做 Google 正在做的事情。创建一个 PHP 文件,在所有 3 个域上设置 cookie。然后在要设置主题的域上,创建一个 HTML 文件,该文件将加载在其他 2 个域上设置 cookie 的 PHP 文件。示例:

<html>
   <head></head>
   <body>
      <p>Please wait.....</p>
      <img src="http://domain2.com/setcookie.php?theme=whateveryourthemehere" />
      <img src="http://domain3.com/setcookie.php?theme=whateveryourthemehere" />
   </body>
</html>

然后在 body 标签上添加一个 onload 回调。仅当图像完全加载时,即在其他 2 个域上设置 cookie 时,文档才会加载。加载回调:

<head>
   <script>
   function loadComplete()
      window.location="http://domain1.com";//URL of domain1
   
   </script>
</head>
<body onload="loadComplete()">

setcookie.php

我们使用这样的 PHP 文件在其他域上设置 cookie:

<?php
if(isset($_GET['theme']))
   setcookie("theme", $_GET['theme'], time()+3600);

?>

现在在三个域上设置了 cookie。

【讨论】:

如果启用了“阻止第三方 cookie”功能,这将不起作用。 只是another answer to another question 的复制粘贴。本题没有“三域”。【参考方案9】:

您可以尝试使用图像标签将 cookie val 推送到另一个域。

尝试执行此操作时,您的里程可能会有所不同,因为某些浏览器要求您在 WebApp2 域上拥有正确的 P3P Policy,否则浏览器将拒绝 cookie。

如果您查看 plus.google.com p3p 政策,您会发现他们的政策是:

CP="这不是 P3P 政策!有关详细信息,请参阅 http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657。"

这是他们对这些跨域请求的 +1 按钮使用的策略。

另一个警告是,如果您在 https 上,请确保图像标签也指向 https 地址,否则 cookie 将不会设置。

【讨论】:

想详细说明一下吗?【参考方案10】:

how Facebook does it here on nfriedly.com 有一个不错的概述

还有浏览器指纹识别,它与 cookie 不同,但其作用相似,因为它可以帮助您以相当确定的程度识别用户。有一个帖子 here on Stack Overflow 引用了一种指纹识别方法

【讨论】:

【参考方案11】:

可以使用不可见的 iframe 来获取 cookie。假设有两个域,a.com 和 b.com。对于 a.com 域的 index.html 可以添加(注意 height=0 width=0):

<iframe  id="iframe" src="http://b.com" ></iframe>

假设http://b.com 设置了cookie,您的网站将获得b.com cookie。

接下来是通过 javascript 在 iframe 中操作网站。如果一个人不拥有第二个域,则 iframe 内部的操作可能会成为一项挑战。但是,如果可以访问两个域,则在 iframe 的 src 中引用正确的网页应该提供一个想要获取的 cookie。

【讨论】:

只是一个警告:Safari 上的 iframe 中的 cookie 存在一些严重问题。它们显然不能跨域工作。【参考方案12】:
function GetOrder(status, filter) 
    var isValid = true; //isValidGuid(customerId);
    if (isValid) 
        var refundhtmlstr = '';
        //varsURL = ApiPath + '/api/Orders/Customer/' + customerId + '?status=' + status + '&filter=' + filter;
        varsURL = ApiPath + '/api/Orders/Customer?status=' + status + '&filter=' + filter;
        $.ajax(
            type: "GET",
            //url: ApiPath + '/api/Orders/Customer/' + customerId + '?status=' + status + '&filter=' + filter,
            url: ApiPath + '/api/Orders/Customer?status=' + status + '&filter=' + filter,
            dataType: "json",
            crossDomain: true,
            xhrFields: 
                withCredentials: true
            ,
            success: function (data) 
                var htmlStr = '';
                if (data == null || data.Count === 0) 
                    htmlStr = '<div class="card"><div class="card-header">Bu kriterlere uygun sipariş bulunamadı.</div></div>';
                
                else 
                    $('#ReturnPolicyBtnUrl').attr('href', data.ReturnPolicyBtnUrl);
                    var groupedData = data.OrderDto.sort(function (x, y) 
                        return new Date(y.OrderDate) - new Date(x.OrderDate);
                    );
                    groupedData = _.groupBy(data.OrderDto, function (d)  return toMonthStr(d.OrderDate) );
                    localStorage['orderData'] = JSON.stringify(data.OrderDto);

                    $.each(groupedData, function (key, val) 

                        var sortedData = groupedData[key].sort(function (x, y) 
                            return new Date(y.OrderDate) - new Date(x.OrderDate);
                        );
                        htmlStr += '<div class="card-header">' + key + '</div>';
                        $.each(sortedData, function (keyitem, valitem) 
                            //Date Convertions
                            if (valitem.StatusDesc != null) 
                                valitem.StatusDesc = valitem.StatusDesc;
                            

                            var date = valitem.OrderDate;
                            date = date.substring(0, 10).split('-');
                            date = date[2] + '.' + date[1] + '.' + date[0];
                            htmlStr += '<div class="col-lg-12 col-md-12 col-xs-12 col-sm-12 card-item clearfix ">' +
                        //'<div class="card-item-head"><span class="order-head">Sipariş No: <a href="ViewOrderDetails.html?CustomerId=' + customerId + '&OrderNo=' + valitem.OrderNumber + '" >' + valitem.OrderNumber + '</a></span><span class="order-date">' + date + '</span></div>' +
                        '<div class="card-item-head"><span class="order-head">Sipariş No: <a href="ViewOrderDetails.html?OrderNo=' + valitem.OrderNumber + '" >' + valitem.OrderNumber + '</a></span><span class="order-date">' + date + '</span></div>' +
                        '<div class="card-item-head-desc">' + valitem.StatusDesc + '</div>' +
                        '<div class="card-item-body">' +
                            '<div class="slider responsive">';
                            var i = 0;
                            $.each(valitem.ItemList, function (keylineitem, vallineitem) 
                                var imageUrl = vallineitem.ProductImageUrl.replace('size', 200);
                                htmlStr += '<div><img src="' + imageUrl + '" ><span class="img-desc">' + ProductNameStr(vallineitem.ProductName) + '</span></div>';
                                i++;
                            );
                            htmlStr += '</div>' +
                        '</div>' +
                    '</div>';
                        );
                    );

                    $.each(data.OrderDto, function (key, value) 
                        if (value.IsSAPMigrationflag === true) 
                            refundhtmlstr = '<div class="notify-reason"><span class="note"><B>Notification : </B> Geçmiş siparişleriniz yükleniyor.  Lütfen kısa bir süre sonra tekrar kontrol ediniz. Teşekkürler. </span></div>';
                        
                    );
                
                $('#orders').html(htmlStr);
                $("#notification").html(refundhtmlstr);
                ApplySlide();
            ,
            error: function () 
                console.log("System Failure");
            
        );
    

Web.config

包括 UI 源并将 Allow Crentials 设置为 true

<httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="http://burada.com" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
        <add name="Access-Control-Allow-Credentials" value="true" />
      </customHeaders>
    </httpProtocol>

【讨论】:

【参考方案13】:

我创建了一个 NPM 模块,它允许您跨域共享本地存储的数据: https://www.npmjs.com/package/cookie-toss

通过使用托管在域 A 上的 iframe,您可以将所有用户数据存储在域 A 上,并通过向域 A iframe 发布请求来引用该数据。

因此,域 B、C 等可以注入 iframe 并向其发布请求以存储和访问所需的数据。域 A 成为所有共享数据的中心。

通过域 A 内的域白名单,您可以确保只有您的依赖站点才能访问域 A 上的数据。

诀窍是将代码放在域 A 上的 iframe 内,该代码能够识别正在请求的数据。上述 NPM 模块中的 README 更深入地介绍了该过程。

希望这会有所帮助!

【讨论】:

【参考方案14】:

除了@Ludovic(已批准的答案)答案,我们还需要在获取 set-cookies 标头时再检查一个选项,

set-cookie: SESSIONID=60B2E91C53B976B444144063; Path=/dev/api/abc; HttpOnly

同时检查 Path 属性值。这应该与您的 API 起始上下文路径相同,如下所示

https://www.example.com/dev/api/abc/v1/users/123

或在不确定上下文路径时使用以下值

Path=/;

【讨论】:

【参考方案15】:

由于很难做 3rd 方 cookie,而且一些浏览器也不允许这样做。

您可以尝试将它们存储在 HTML5 本地存储中,然后将它们与前端应用的每个请求一起发送。

【讨论】:

【参考方案16】:

Web Api 中读取Cookie

var cookie = actionContext.Request.Headers.GetCookies("newhbsslv1");


                    Logger.Log("Cookie  " + cookie, LoggerLevel.Info);
                    Logger.Log("Cookie count  " + cookie.Count, LoggerLevel.Info);

                    if (cookie != null && cookie.Count > 0)
                    
                        Logger.Log("Befor For  " , LoggerLevel.Info);
                        foreach (var perCookie in cookie[0].Cookies)
                        
                            Logger.Log("perCookie  " + perCookie, LoggerLevel.Info);

                            if (perCookie.Name == "newhbsslv1")
                            
                                strToken = perCookie.Value;
                            
                        
                    

【讨论】:

这不处理在两个不同域上使用的 OP 问题

以上是关于跨域 Cookie的主要内容,如果未能解决你的问题,请参考以下文章

Cookie和Session

Django之Session与Cookie

在servlet中获取cookie

Java Web 自动登录

在servlet中获取cookie

Cookie&Session