Safari 第三方 cookie iframe 技巧不再有效?

因此,这是对“我如何让第三方 cookie 在 Safari 中工作”这个问题的第十次报复,但我再次提出这个问题,因为我认为竞争环境已经发生了变化,也许是在2012年2月之后。在 Safari 中获得第三方 cookie 的一个标准技巧如下: 使用一些 javascript 来 POST 到一个隐藏的 iframe。它(过去)欺骗 Safari,使其认为用户已经与第三方内容进行了交互,因此允许设置 cookie。

好好想想这个漏洞已经被关闭,在轻微的丑闻之后,它被揭露,谷歌正在使用这种伎俩与其广告。至少,在使用这个技巧时,我完全无法在 Safari 中设置 cookie。我发现了一些随机的网上帖子,声称苹果正在努力弥补这个漏洞,但我没有找到任何官方文字。

作为备用方案,我甚至尝试重新设计主要的第三方框架,这样你必须在内容加载之前点击一个按钮,但即使是那种程度的直接交互也不足以融化 Safari 冰冷的心。

那么,有人确切知道 Safari 是否真的堵住了这个漏洞吗?如果有,是否还有其他解决方案(除了在每个请求中手动包含会话 ID 之外) ?

123106 次浏览

谷歌实际上在这件事上泄露了秘密。他们用了一段时间来访问追踪 cookie。苹果公司几乎立刻修好了它

原版 华尔街日报

我用.htaccess 欺骗了 Safari:

#http://www.w3.org/P3P/validator.html
<IfModule mod_headers.c>
Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID CUR ADM DEV OUR BUS\""
Header set Set-Cookie "test_cookie=1"
</IfModule>

对我来说也不管用了。我所有的应用程序都丢失了 Safari 中的会话,并且正在从 Facebook 中重定向。由于我急于修复这些应用程序,我目前正在寻找解决方案。有消息我会通知你的。

编辑(2012-04-06) : 显然苹果用5.1.4“修复”了它。我确信这是对 Google 事件的反应: “在执行 cookie 政策时存在问题。如果 Safari 中的“ Block Cookies”首选项被设置为默认设置“ From Third Party and Advertising”,那么第三方网站可以设置 Cookies。http://support.apple.com/kb/HT5190

您可以通过添加消息头作为 p3p 策略来解决这个问题。.我有同样的问题在 Safari,所以后添加顶部的文件头已经解决了我的问题。

<?php
header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
?>

你说你愿意让你的用户在内容加载之前点击一个按钮。我的解决方案是用一个按钮打开一个新的浏览器窗口。该窗口为我的域设置 Cookie,刷新打开器,然后关闭。

所以你的主要剧本应该是这样的:

<?php if(count($_COOKIE) > 0): ?>
<!--Main Content Stuff-->
<?php else: ?>
<a href="/safari_cookie_fix.php" target="_blank">Click here to load content</a>
<?php endif ?>

然后 safari _ cookie _ fix.php 看起来像:

<?php
setcookie("safari_test", "1");
?>
<html>
<head>
<title>Safari Fix</title>
<script type="text/javascript" src="/libraries/prototype.min.js"></script>
</head>
<body>
<script type="text/javascript">
document.observe('dom:loaded', function(){
window.opener.location.reload();
window.close();
})
</script>
This window should close automatically
</body>
</html>

我也遇到过同样的问题,今天我找到了一个适合我的解决方案。如果用户代理包含 Safari而没有设置 cookie,我将用户重定向到 OAuth 对话框:

<?php if ( ! count($_COOKIE) > 0 && strpos($_SERVER['HTTP_USER_AGENT'], 'Safari')) { ?>
<script type="text/javascript">
window.top.location.href = 'https://www.facebook.com/dialog/oauth/?client_id=APP_ID&redirect_uri=MY_TAB_URL&scope=SCOPE';
</script>
<?php } ?>

在身份验证和请求权限之后,OAuth 对话框将重定向到顶部位置的 URI。所以设置 cookie 是可能的。对于我们所有的画布和页面选项卡应用程序,我已经包含了以下脚本:

<script type="text/javascript">
if (top.location.href==location.href) top.location.href = 'MY_TAB_URL';
</script>

因此,用户将被重定向到 Facebook 页面选项卡与 已设置的有效 cookie和签署的请求再次张贴。

我终于找到了和萨沙提供的类似的解决方案, 然而,由于我是在 PHP 中明确设置 cookie 的,所以只需要稍微调整一下:

// excecute this code if user has not authorized the application yet
// $facebook object must have been created before


$accessToken = $_COOKIE['access_token']


if ( empty($accessToken) && strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') ) {


$accessToken = $facebook->getAccessToken();
$redirectUri = 'https://URL_WHERE_APP_IS_LOCATED?access_token=' . $accessToken;


} else {


$redirectUri = 'https://apps.facebook.com/APP_NAMESPACE/';


}


// generate link to auth dialog
$linkToOauthDialog = $facebook->getLoginUrl(
array(
'scope'         =>  SCOPE_PARAMS,
'redirect_uri'  =>  $redirectUri
)
);


echo '<script>window.top.location.href="' . $linkToOauthDialog . '";</script>';

这样做的目的是检查当浏览器是 safari 时 cookie 是否可用。 在下一步中,我们将处于应用程序域上,即作为上面的 URL _ WHERE _ APP _ IS _ LOCATED 提供的 URI。

if (isset($_GET['accessToken'])) {


// cookie has a lifetime of only 10 seconds, so that after
// authorization it will disappear
setcookie("access_token", $_GET['accessToken'], 10);


} else {


// depending on your application specific requirements
// redirect, call or execute authorization code again
// with the cookie now set, this should return FB Graph results


}

因此,在重定向到应用程序域之后,将显式设置 cookie,并将用户重定向到授权进程。

在我的例子中(因为我使用的是 CakePHP,但是它应该可以很好地与任何其他 MVC 框架一起工作) ,我再次调用登录操作,在那里 FB 授权将在另一次执行,这一次由于现有的 cookie 而成功。

在授权该应用程序一次后,我使用 Safari (5.1.6)的应用程序没有任何问题

希望这能帮到大家。

只是想在这里留下一个简单的工作解决方案,不需要用户交互

正如我在 我发的帖子中所说:

基本上你所需要做的就是在 top.location 上加载你的页面,创建会话并将它重定向回 facebook。

将这段代码添加到您的 index.php顶部,并将 $page_url设置为您的应用程序的最终选项卡/应用程序 URL,您将看到您的应用程序将工作没有任何问题。

<?php
// START SAFARI SESSION FIX
session_start();
$page_url = "http://www.facebook.com/pages/.../...?sk=app_...";
if (isset($_GET["start_session"]))
die(header("Location:" . $page_url));


if (!isset($_GET["sid"]))
die(header("Location:?sid=" . session_id()));
$sid = session_id();
if (empty($sid) || $_GET["sid"] != $sid):
?>
<script>
top.window.location="?start_session=true";
</script>
<?php
endif;
// END SAFARI SESSION FIX
?>

注意: 这是为 facebook 设计的,但实际上在任何其他类似的情况下都可以使用。


编辑2012年12月20日-维护已签署的请求:

上面的代码不维护请求后的数据,如果您的应用程序依赖于已签名的请求,您可以尝试下面的代码:

注意: 这仍在正确测试中,可能不如第一个版本稳定。 自行承担使用风险/欢迎反馈

(感谢 CBroe为我指明了正确的方向,使我能够改进解决方案)

// Start Session Fix
session_start();
$page_url = "http://www.facebook.com/pages/.../...?sk=app_...";
if (isset($_GET["start_session"]))
die(header("Location:" . $page_url));
$sid = session_id();
if (!isset($_GET["sid"]))
{
if(isset($_POST["signed_request"]))
$_SESSION["signed_request"] = $_POST["signed_request"];
die(header("Location:?sid=" . $sid));
}
if (empty($sid) || $_GET["sid"] != $sid)
die('<script>top.window.location="?start_session=true";</script>');
// End Session Fix

这是我用的一些代码。我发现,如果我从我的网站设置任何 cookie,那么 cookie 神奇地工作在 iframe 从那时起。

Http://developsocialapps.com/foundations-of-a-facebook-app-framework/

 if (isset($_GET['setdefaultcookie'])) {
// top level page, set default cookie then redirect back to canvas page
setcookie ('default',"1",0,"/");
$url = substr($_SERVER['REQUEST_URI'],strrpos($_SERVER['REQUEST_URI'],"/")+1);
$url = str_replace("setdefaultcookie","defaultcookieset",$url);
$url = $facebookapp->getCanvasUrl($url);
echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
exit();
} else if ((!isset($_COOKIE['default'])) && (!isset($_GET['defaultcookieset']))) {
// no default cookie, so we need to redirect to top level and set
$url = $_SERVER['REQUEST_URI'];
if (strpos($url,"?") === false) $url .= "?";
else $url .= "&";
$url .= "setdefaultcookie=1";
echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
exit();
}

我最近在 Safari 上遇到了同样的问题。我想出的解决方案是基于本地存储 HTML5 API 的。使用本地存储可以模拟 cookie。

以下是我的博客文章详细内容: http://log.scalemotion.com/2012/10/how-to-trick-safari-and-set-3rd-party.html

对于我的具体情况,我通过使用 window.postMessage ()并消除任何用户交互来解决问题。请注意,只有在父窗口中以某种方式执行 js 时,这种方法才会有效。要么让它包含来自您的域的 js,要么您可以直接访问源代码。

在 iframe (domain-b)中,我检查是否存在 cookie,如果没有设置,将向父(domain-a)发送一条 postMessage。例如:

if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1
&& document.cookie.indexOf("safari_cookie_fix") < 0) {
window.parent.postMessage(JSON.stringify({ event: "safariCookieFix", data: {} }));
}

然后在父窗口(domain-a)中侦听事件。

if (typeof window.addEventListener !== "undefined") {
window.addEventListener("message", messageReceived, false);
}


function messageReceived (e) {
var data;


if (e.origin !== "http://www.domain-b.com") {
return;
}


try {
data = JSON.parse(e.data);
}
catch (err) {
return;
}


if (typeof data !== "object" || typeof data.event !== "string" || typeof data.data === "undefined") {
return;
}


if (data.event === "safariCookieFix") {
window.location.href = e.origin + "/safari/cookiefix"; // Or whatever your url is
return;
}
}

最后,在服务器上( http://www.domain-b.com/safari/cookiefix )设置 cookie 并重定向回用户来自的地方。下面的例子是使用 ASP.NET MVC

public class SafariController : Controller
{
[HttpGet]
public ActionResult CookieFix()
{
Response.Cookies.Add(new HttpCookie("safari_cookie_fix", "1"));


return Redirect(Request.UrlReferrer != null ? Request.UrlReferrer.OriginalString : "http://www.domain-a.com/");
}


}

在 Ruby on Rails 控制器中,您可以使用:

private


before_filter :safari_cookie_fix


def safari_cookie_fix
user_agent = UserAgent.parse(request.user_agent) # Uses useragent gem!
if user_agent.browser == 'Safari' # we apply the fix..
return if session[:safari_cookie_fixed] # it is already fixed.. continue
if params[:safari_cookie_fix].present? # we should be top window and able to set cookies.. so fix the issue :)
session[:safari_cookie_fixed] = true
redirect_to params[:return_to]
else
# Redirect the top frame to your server..
render :text => "<script>alert('start redirect');top.window.location='?safari_cookie_fix=true&return_to=#{set_your_return_url}';</script>"
end
end
end

我在运行 iOS 的设备上遇到了这个问题。我做了一个商店,是嵌入在一个正常的网站使用一个 iframe。不知为何,在每次页面加载时,用户都会得到一个新的 sessionid,结果导致用户在进程的中途被卡住,因为一些值没有出现在会话中。

我尝试了一些在这个页面给出的解决方案,但弹出窗口不是很好的工作在 iPad 上,我需要最透明的解决方案。

我用重定向解决了。嵌入我的网站的网站必须首先将用户重定向到我的网站,所以 头儿帧包含我的网站的 URL,在那里我设置一个 cookie 并将用户重定向到嵌入我的网站的适当页面,这是通过网址传递的。

PHP 代码示例

远程网站将用户重定向到

http://clientname.example.com/init.php?redir=http://www.domain.com/shop/frame

Init.php

<?php
// set a cookie for a year
setcookie('initialized','1',time() + 3600 * 24 * 365, '/', '.domain.com', false, false);
header('location: ' . $_GET['redir']);
die;

用户最终到了我的站点嵌入的 http://www.domain.com/shop/frame上,按照它应该的方式存储会话并且吃饼干。

希望这对谁有帮助。

我决定一起去掉 $_SESSION变量,并在 memcache 周围编写了一个包装器来模拟会话。

检查 https://github.com/manpreetssethi/utils/blob/master/Session_manager.php

用例: 当用户登录到应用程序时,使用 Session _ manager 存储已签名的请求,由于它在缓存中,因此您可以在以后的任何页面上访问它。

注意: 这在 Safari 中私下浏览时不起作用,因为每次页面重新加载时 session _ id 都会重置。(愚蠢的狩猎)

让我来分享一下我在 ASP.NET MVC 4中的修复方法。 接下来的代码添加在主布局的头脚本部分:

@if (Request.Browser.Browser=="Safari")
{
string pageUrl = Request.Url.GetLeftPart(UriPartial.Path);
if (Request.Params["safarifix"] != null && Request.Params["safarifix"] == "doSafariFix")
{
Session["IsActiveSession"] = true;
Response.Redirect(pageUrl);
Response.End();
}
else if(Session["IsActiveSession"]==null)
{
<script>top.window.location = "?safarifix=doSafariFix";</script>
}
}

我使用了修改过的(在链接中添加了 sign _ request 参数) WhitEagle 的技巧,它在 safari 中运行良好,但在这种情况下 IE 会不断刷新页面。因此,我对狩猎和 Internet Explorer 的解决方案是:

$fbapplink = 'https://apps.facebook.com/[appnamespace]/';
$isms = stripos($_SERVER['HTTP_USER_AGENT'], 'msie') !== false;


// safari fix
if(! $isms  && !isset($_SESSION['signed_request'])) {


if (isset($_GET["start_session"])) {
$_SESSION['signed_request'] = $_GET['signed_request'];
die(header("Location:" . $fbapplink ));


}
if (!isset($_GET["sid"])) {
die(header("Location:?sid=" . session_id() . '&signed_request='.$_REQUEST['signed_request']));
}
$sid = session_id();
if (empty($sid) || $_GET["sid"] != $sid) {
?>
<script>
top.window.location="?start_session=true";
</script>
<?php
exit;
}
}


// IE fix
header('P3P: CP="CAO PSA OUR"');
header('P3P: CP="HONK"');




.. later in the code


$sr = $_REQUEST['signed_request'];
if($sr) {
$_SESSION['signed_request'] = $sr;
} else {
$sr = $_SESSION['signed_request'];
}

一个稍微简单一点的 PHP 版本:

if (!isset($_COOKIE, $_COOKIE['PHPSESSID'])) {
print '<script>top.window.location="https://example.com/?start_session=true";</script>';
exit();
}


if (isset($_GET['start_session'])) {
header("Location: https://apps.facebook.com/YOUR_APP_ID/");
exit();
}

这种解决办法适用于某些情况——如果可能的话:

如果 iframe 内容页面使用包含 iframe 的页面的子域,那么 cookie 不再被阻塞。

我也一直在遭受这个问题,但最终得到了解决方案,最初直接加载的 iframe 网址在浏览器中像小弹出窗口,然后只访问内部的会话值 iframe。

我已经找到了这个问题的完美答案,这一切都要感谢一个叫艾伦的家伙,他值得这里所有的荣誉。 (http://www.allannienhuis.com/archives/2013/11/03/blocked-3rd-party-session-cookies-in-iframes/)

他的解决方案简单易懂。

在 iframe 内容服务器(域2)上,在根域级别添加一个名为 Startsession.php的文件,其中包含:

<?php
// startsession.php
session_start();
$_SESSION['ensure_session'] = true;
die(header('location: '.$_GET['return']));

现在,在包含 iframe (domain1)的顶级网站上,对包含 iframe 的页面的调用应该类似于:

<a href="https://domain2/startsession.php?return=http://domain1/pageWithiFrame.html">page with iFrame</a>

就是这样! 简单:)

这样做的原因是,您将浏览器定向到第三方 URL,因此告诉它在在 iframe 中显示来自该 URL 的内容之前要信任它。

Safari 现在屏蔽所有第三方 cookie。您只能使用 StorageAPI 尝试让用户访问他们的第三方 cookie。

Https://www.infoq.com/news/2020/04/safari-third-party-cookies-block/

一些我在现有的答案中没有看到清楚的上下文(自2012年以来也发生了很多变化!) :

如果您可以同时控制第三方 iframe 和父页面 (也就是说,您可以在父页面上插入 JavaScript) ,那么有几个变通方法可用。我建议最优雅的方法是使用@Frank 的回答中描述的 postMessage API,因为 a)这不需要任何重定向,b)不需要任何用户交互。

如果你不能同时控制第三方 iframe 和父页面 ,例如你有一个部件托管在一个你不能控制的网站上,那么这里张贴的大部分答案将不能在 截至2020年5月的旅行中工作,而将在 在2022年前后停止使用 Chrome中工作。也就是说,除非用户已经访问了您的域或与 iframe 进行了交互,否则无法设置 cookie。然而,有一些商业服务提供了解决这个问题的解决方案,例如: CloudCookie.io