Safari 3rd party cookie iframe技巧不再起作用?


137

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

认为这个漏洞已经随着轻微的丑闻而被掩盖,当时有消息称谷歌在广告中使用了这种技巧。至少,在使用此技巧时,我完全无法在Safari中设置Cookie。我发掘了一些互联网随机发帖,声称苹果正在努力弥补漏洞,但我没有找到任何官方消息。

作为后备,我什至尝试重新设计主要的第三方框架,以便您在内容加载之前必须单击一个按钮,但是即使直接交互的程度也不足以融化Safari的冷漠之心。

那么,有人能确定Safari是否确实消除了这个漏洞吗?如果是这样,还有其他解决方法(除了在每个请求中手动包括会话ID之外)?


21
根据定义,使用需要cookie的第三方iframe绝对不是安全攻击!我们在一个iframe中使用了一个网上商店,该网上商店在多个不同的域中使用,并且由于Safari出现了各种各样的问题,因此我对这个(合法)问题的答案也非常感兴趣。
mscha 2012年

14
几乎所有构建Facebook应用程序的人都使用Safari遇到此问题。Facebook应用程序在iframe中运行,并且根据定义,它们均来自第三方。这就是为什么在Facebook应用程序上对Safari的支持有点参差不齐:您不能使用Cookie的原因。
gs hurley

我无法在Safari 5.1.7上重现此问题。它使用默认的“无第三方Cookie”设置接受来自我的Facebook应用iframe的Cookie。但是具有相同设置的Chrome 19.0.1084.46会阻止cookie。
叶夫根尼·谢德涅夫

4
选中了(非常感谢)非默认的“阻止第三方Cookie和网站数据”选项的Chrome 19+比Safari的默认“阻止第三方和广告客户的Cookie”设置更为苛刻。在chrome中,即使您访问了第三方域名并设置了Cookie,也不会将其传输到iframe。用户实际上必须在她的Chrome安全设置中为您的域添加“例外”。
亚伦·吉布拉特

您表示2012年2月是什么意思?Safari是否发生技术变化或法律变化?
液体

Answers:


51

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

正如我在帖子中所说:

基本上,您需要做的就是将页面加载到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日-维护已签署的请求:

上面的代码不维护请求的发布数据,并且如果您的应用程序依赖于签名的请求,则可以放慢signed_request的尝试:

注意:此版本仍在正确测试中,可能不如第一个版本稳定。使用后果自负/感谢您的反馈。

(感谢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

谢谢您,它的魅力
十足,

所以这个基本上可以与几个重定向一起使用,对吗?
亚伦·吉布拉特

1
@hugoderhungrige,欢迎您,我刚刚添加了一个新版本,如果您需要在应用程序上维护已签名的请求,请随时查看。
Diogo Raminhos

1
@CBroe非常感谢您指出这一点!您说对了,它确实有效,因为通过第二个请求,用户已经启动了会话!我猜想“最糟糕的盲人是不想见的人”。
Diogo Raminhos 2012年

1
@Whiteagle您能否提供一个演示1和2的测试用例?
Gajus 2014年

35

您说过,您希望用户在内容加载之前单击一个按钮。我的解决方案是用一个按钮打开新的浏览器窗口。该窗口将为我的域设置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>

我在想类似的东西。完美地工作。我将其与权限对话框一起加载。谢谢!
vwoelm'4

似乎这样也可以解决Safari的设置问题,而且一旦成为常识,就可以像其他“解决方案”一样消除知识。我正在寻找一种完全不同的解决方案,因为越来越明显的是,即使以适当的方式使用,第三方Cookie也是魔鬼。
LocalPCGuy 2012年

1
此解决方案对我有用,但是弹出窗口是否必要?您能否将iframe重定向到safari页面,设置cookie,然后使用重定向标题重定向回到游戏?还是您需要弹出窗口,以便用户可以某种形式直接与服务器联系?
安东尼·黑斯廷斯

这运作良好;幸运的是,在我的应用程序中,按下按钮来初始化它并不重要。
littlered 2012年

@LocalPCGuy我不太确定它会像其他解决方案一样被砍掉,因为它要求用户实际单击/与页面交互才能阻止弹出窗口。Safari提出的这种解决方案似乎效果很好:广告客户将无法秘密设置跨域Cookie,嵌入式应用将需要用户进行交互才能设置Cookie。
亚伦·吉布拉特

15

我用.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):显然,Apple用5.1.4“修复”了它。我确信这是对Google的回应:“其Cookie政策的实施存在问题。如果Safari中的“阻止Cookie”首选项设置为默认设置“来自第三方和广告商” 。http://support.apple.com/kb/HT5190


1
显然,Apple用5.1.4“修复”了它。我确信这是对Google的回应: “其Cookie政策的实施存在问题。如果Safari中的“阻止Cookie”首选项设置为默认设置“来自第三方和广告商”
。support.apple.com/

1
因此,我认为vwoelm的这一评论与我一直在寻找的答案最接近。首先,我想确认一下Apple是否已经彻底消除了漏洞,而提及Apple支持文章就是这样。我的问题的第二部分虽然仍然适用。解决方法的选择范围是什么。显然,我们可以将会话ID编码为GET / POST参数,但还有其他选择。本地存储是否可以在这种情况下工作?Flash Cookie?
gs hurley

@vwoelm:这确实是我一直在寻找(但不希望)的答案。如果您将其放在答案中而不是评论中,我将为您分配赏金。
mscha 2012年

3
@gshurley我认为通过GET / POST参数发送会话ID是唯一的选择。它是不安全的,但随后Facebook再次迫使我们在没有SSL的情况下提供画布应用程序。而且,除了通过入侵Facebook应用程序还能真正得到什么,哈哈?我们受苹果的摆布,他们目前处于残酷状态。
hekevintran

14

在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

Safari中的会话是否存在类似的问题?
Rails初学者

您可以将set_your_return_url替换为request.env ['HTTP_REFERER'],因为我们位于iframe中:-D
Luc Boissaye,2015年

我已经尝试过此解决方案,唯一的问题是我无法返回到父iframe网址。Rails中是否有一种方法可以获取父iframe的网址?谢谢
idejuan 2015年

我花了一些时间,但最终我发现最简单的方法(TMO)是将其作为查询字符串参数添加到Rails应用程序的重定向URL中。干净,干净。
Guyaloni

1
此答案将创建一个开放重定向器,这是一个常见的安全问题。危险代码是redirect_to params[:return_to]。需要对照要重定向到的安全场所白名单检查该参数。参见owasp.org/index.php/…–
菲雅

13

对于我的特定情况,我通过使用window.postMessage()并消除了任何用户交互来解决了该问题。请注意,这仅在您可以以某种方式在父窗口中执行js时才有效。通过在您的域中包含一个js,或者您可以直接访问该源。

在iframe(域b)中,我检查是否存在cookie,如果未设置cookie,则会将postMessage发送给父域(域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: {} }));
}

然后在父窗口(域-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/");
    }

}

您对postMessage的调用没有语法错误?
akousmata 2015年

需要添加第二个参数"*"PostMessage修复语法错误
Michael Baldry

希望我能给予更多答复。这是解决此问题的最简单,最正确的方法。谢谢您的发布!
AaronP '16

9

我遇到了同样的问题,今天我发现了一个对我来说很好的修复程序。如果用户代理包含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。对于我们所有的canvas和page选项卡应用程序,我已经包含以下脚本:

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

因此,将使用已设置有效cookie的用户再次将其重定向到Facebook页面选项卡,并再次发布签名的请求。


伟大的工作。我更新了用户代理检查,以确保Chrome也不在用户代理中,因为Chrome在用户代理字符串中也包含Safari。重定向到您的域页面,设置所需的cookie /启动会话,然后重定向到apps.facebook.com上的应用程序,就像一个魅力。看起来很傻,但是效果很好。感谢Sascha的提示!
迈克

7

我终于找到了与Sascha提供的解决方案类似的解决方案,但是做了一些调整,因为我在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框架配合使用),我再次调用login操作,在该操作中再次执行FB授权,由于现有的cookie,这次成功。

一次性授权应用程序后,将应用程序与Safari(5.1.6)一起使用时,我再也没有任何问题

希望对任何人都有帮助。


1
这对我有用!谢谢!!..我在Safari 5.1.7中遇到了这个问题。...现在已经解决了!
Khalizar 2012年

5

我在运行iOS的设备上遇到此问题。我使用iframe在一家普通网站上嵌入了一家商店。不知何故,在每个页面加载中,用户都有一个新的sessionid,这导致用户在进程中途陷入困境,因为会话中不存在某些值。

我尝试了此页面上提供的一些解决方案,但是弹出窗口在iPad上无法很好地工作,因此我需要最透明的解决方案。

我使用重定向解决了它。嵌入我的网站的网站必须首先将用户重定向到我的网站,因此顶部框架包含指向我的网站的网址,我在其中设置一个cookie并将用户重定向到嵌入我的网站的网站上的正确页面,通过URL。

示例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我的网站的嵌入位置,按原样存储了会话并吃了cookie。

希望这对某人有帮助。


3

让我在ASP.NET MVC 4中分享我的修复。主要思想是为PHP提供正确答案。在脚本部分标题附近的主布局中添加的下一个代码:

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

3

此解决方案在某些情况下适用-可能的话:

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


这是有用的信息-正是我想要的。尽管我确定大多数人都无法更改域,但这是我要做的。让我要嵌入的网站创建一个<mycompany>。<theircompany> .com子域,该子域映射到我的IP并在我这边映射一个VHost,并在iFrame中使用该URL。复杂,但应防弹。
Elocution Safari 2015年

1
如果您将https与子域一起使用,这会变得很复杂,因为子域服务器将需要SSL证书来进行匹配。
罗杰·哈利伯顿

1

谷歌实际上是把这只猫从包里拿出来的。他们使用它一段时间来访问跟踪cookie。它几乎立即由Apple修复了。

华尔街日报原始帖子


我知道Google的事,但我还没有看到有关Apple实际“解决”此问题(并破坏了互联网的一半)的任何信息。你有什么细节吗?
mscha 2012年

1

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

1

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

1

我已经找到了一个完美的答案,这要归功于一个名叫艾伦(Allan)的家伙,他在这里应该得到所有的荣誉。(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中显示来自其的内容之前信任它。


0

我使用了修改过的(向链接添加了signed_request参数)Whiteagle的技巧,并且可以很好地用于野生动物园,但是在这种情况下,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'];
}

0

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



-2

我决定摆脱 $_SESSION所有变量,并在memcache周围写了一个包装器来模拟会话。

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

用例:用户登陆应用程序时,使用Session_manager存储已签名的请求,由于该请求位于缓存中,因此您可以在以后的任何页面上对其进行访问。

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


-9

您可以通过将标题添加为p3p策略来解决此问题。

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

您确定(仍然)可以在最新版本的Safari上运行吗?对于IE,我们已经有了P3P标头很多年了,但是Safari仍然无法使用。
mscha 2012年

2
尝试清除所有cookie,然后再次进行测试。如果您的浏览器已经为您的iframed网站安装了cookie,则该问题不会自行显示。
rmarscher 2012年

嗯,我也无法使用P3P标头。他们仍然在IE中工作!
赛斯·布朗

2
这曾经工作。但是对于Safari的最新版本则不是。清除缓存,然后重试....
chantheman 2012年

2
我想说明这个解决方案有效。确保您删除野生动物园中的所有cookie。然后尝试这个。
dnuske
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.