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

所以这是“我如何获得第三方cookie在Safari上工作”的第十八次复仇,但我再次提出要求,因为我认为游戏领域已经发生了变化,也许在2012年2月之后。获得第三名的标准技巧之一在Safari浏览器中的第三方cookies如下所示:使用一些JavaScript来发布到隐藏的iframe。 它(用来)诱使Safari认为用户与第三方内容进行了交互,然后允许设置cookie。

认为这个漏洞已经在轻微的丑闻之后closures了,因为这个丑闻揭露了Google在广告中使用这个伎俩。 至less在使用这个技巧时,我已经完全无法在Safari中设置cookie了。 我发掘了一些随机的networkingpost,声称苹果正在堵塞这个漏洞,但是我还没有发现任何官方的说法。

作为一个后备,我甚至试图重新devise主要的第三方框架,所以你必须点击一个button,才能加载内容,但即使这样的直接交互水平也不足以融化Safari的冷酷心脏。

那么有没有人知道Safari是否真的closures了这个漏洞? 如果是这样,是否还有其他解决方法(除了在每个请求中手动包含会话ID)?

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

正如我在一篇文章中所说:

基本上所有你需要做的是加载你的网页top.location,创build会话,并重新导向回到Facebook。

将此代码添加到index.php的顶部,并将$page_url设置$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日 – 维护签署的请求:

上面的代码不会维护请求发布数据,并且如果您的应用程序依赖签名请求,则可能会丢失signed_request,随意尝试以下代码:

注意:这仍然是正确testing,可能不稳定比第一个版本。 使用风险自负/反馈表示赞赏。

(感谢CBroe指出我正确的方向,从而改进解决scheme)

 // 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 

你说你愿意让你的用户在内容加载之前点击一个button。 我的解决scheme是有一个button打开一个新的浏览器窗口。 那个窗口为我的域设置了一个cookie,刷新了开启者,然后closures。

所以你的主要脚本可能是这样的:

 <?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> 

我用.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中丢失会话,并redirect到Facebook。 由于我急于修复这些应用程序,目前我正在寻找解决scheme。 我会及时向大家发布。

编辑(2012-04-06):显然苹果公司用5.1.4“固定” 我确信这是对Google的反应:“执行Cookie政策时存在问题。如果Safari中的”阻止Cookie“偏好设置为默认设置”来自第三方和广告商“ http://support.apple.com/kb/HT5190

在你的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 

对于我的具体情况,我通过使用window.postMessage()解决了这个问题,并消除了任何用户交互。 请注意,这只会工作,如果你可以以某种方式在父窗口中执行js。 要么从你的域名中包含一个js,要么直接访问源代码。

在iframe(domain-b)中,我检查是否存在一个cookie,如果没有设置,就会发送一个postMessage给父节点(domain-a)。 例如;

 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并redirect到用户来自的地方。; 下面的例子是使用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/"); } } 

我有同样的问题,今天我发现一个修复程序,对我来说工作正常。 如果用户代理包含Safari并且没有设置Cookie,我将用户redirect到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 } ?> 

在validation和请求权限之后,OAuth对话框将redirect到位于顶部位置的我的URI。 所以设置cookie是可能的。 对于我们所有的canvas和页面标签应用程序,我已经包含了以下脚本:

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

因此,用户将被redirect到Facebook页面标签,并且已经设置有效的cookie ,并且签名的请求被再次发布。

我终于find了一个与Sascha提供的解决scheme类似的解决scheme,但是有一些调整,因为我在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。

 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 } 

所以在redirect到应用程序域之后,会明确设置一个cookie,并将用户redirect到授权过程。

在我的情况下(因为我使用CakePHP,但它应该与其他任何MVC框架一样正常工作)我再次调用FB授权执行的login操作,这次它成功由于现有的cookie。

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

希望可以帮助任何人。

让我分享我在ASP.NET MVC 4中的修复。主要想法就像在正确的PHP答案。 下面的代码添加在脚本部分的header中的主Layout中:

 @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> } } 

这种解决scheme在某些情况下适用 – 如果可能

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

我在运行iOS的设备上遇到了这个问题。 我用一个iframe制作了一个可embedded到普通网站的商店。 不知何故,在每个页面上,用户都得到了一个新的sessionid,导致用户在进程中途停滞不前,因为会话中没有一些值。

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

我解决了它使用redirect。 embedded我的网站的网站必须首先将用户redirect到我的网站,所以顶部框架包含我的网站的url,我设置一个cookie并将用户redirect到embedded我的网站的网站上的正确页面,通过在URL中。

PHP代码示例

远程网站将用户redirect到

 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; 

用户最终在我的网站被embedded的http://www.domain.com/shop/frame ,存储会话,应该和吃cookies。

希望这有助于某人。

谷歌实际上让这只猫从包里掏出。 他们正在使用它一段时间来访问跟踪cookie。 它几乎立即由苹果=

原华尔街日报的post

这是我使用的一些代码。 我发现,如果我从我的网站设置了任何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(); } 

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

我使用了修改(在链接中添加了signed_request参数)Whiteagle的技巧,Safari浏览器可以正常工作,但IE在这种情况下不断刷新页面。 所以我的Safari浏览器和Internet Explorer的解决scheme是:

 $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']; } 

我也一直在遭受这个问题,但终于得到了解决scheme,最初直接加载浏览器中的iframe url像小popup,然后只访问iframe内的会话值。

我已经find了完美的答案,所有这一切都要归功于一个名叫艾伦的人,值得所有的信任。 ( http://www.allannienhuis.com/archives/2013/11/03/blocked-3rd-party-session-cookies-in-iframes/

他的解决scheme简单易懂。

在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中显示内容之前信任它。

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

这里是我的博客文章的详细信息: http : //log.scalemotion.com/2012/10/how-to-trick-safari-and-set-3rd-party.html

我决定一起摆脱$_SESSIONvariables,并写了一个关于memcache的包装来模仿会话。

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

用例:用户login应用程序的那一刻,使用Session_manager存储已签名的请求,并且由于它在caching中,因此您可以在以后的任何页面上访问它。

注意:在Safari浏览器中私人浏览时,这将不起作用,因为session_id在每次页面重新加载时都会重置。 (愚蠢的safari)

你可以通过添加标题为p3p策略来解决这个问题..我对safari有同样的问题,所以在文件顶部添加标题已解决了我的问题。

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