Safari 3rd party cookie iframe trick no longer wor

2019-01-01 06:15发布

So this is the umteenth revenge of the "how do I get 3rd party cookies to work in Safari" question but I'm asking again because I think the playing field has changed, perhaps after February 2012. One of the standard tricks to get 3rd party cookies in Safari was as follows: use some javascript to POST to a hidden iframe. It (used to) trick Safari into thinking that the user had interacted with the 3rd party content and so then allow cookies to be set.

I think this loophole has been closed in the wake of the mild scandal where it was revealed that Google was using that trick with its ads. At the very least, while using this trick I have been completely unable to set cookies in Safari. I unearthed some random internet postings that claimed that Apple was working on closing the loophole but I haven't found any official word.

As a fallback I even tried redesigning the main third party frame so that you had to click on a button before the content would load but even that level of direct interaction was not enough to melt Safari's cold cold heart.

So does anyone know for certain if Safari has indeed closed this loophole? If so, are there other workarounds (other than manually including a session ID in every request)?

19条回答
公子世无双
2楼-- · 2019-01-01 06:47

I recently hit the same issue on Safari. The solution I figured out is based on the Local Storage HTML5 API. Using Local Storage you could emulate cookies.

Here's my blog post with details: http://log.scalemotion.com/2012/10/how-to-trick-safari-and-set-3rd-party.html

查看更多
后来的你喜欢了谁
3楼-- · 2019-01-01 06:48

Here's some code that I use. I found that if I set any cookie from my site, then cookies magically work in the iframe from then on.

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();
    }
查看更多
倾城一夜雪
4楼-- · 2019-01-01 06:52

For my specific situation I resolved the problem by using window.postMessage() and eliminating any user interaction. Note that this will only work if you can somehow execute js in the parent window. Either by having it include a js from your domain, or if you have direct access to the source.

In the iframe (domain-b) i check for the presence of a cookie and if it is not set will send a postMessage to the parent (domain-a). Eg;

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: {} }));
}

Then in the parent window (domain-a) listen for the event.

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

Finally on your server (http://www.domain-b.com/safari/cookiefix) you set the cookie and redirect back to where the user came from. Below example is using 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/");
    }

}
查看更多
心情的温度
5楼-- · 2019-01-01 06:56

I used modified (added signed_request param to the link) Whiteagle's trick and it worked ok for safari, but IE is constantly refreshing the page in that case. So my solution for safari and internet explorer is:

$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'];
}
查看更多
梦该遗忘
6楼-- · 2019-01-01 07:00

I finally went for a similar solution to the one that Sascha provided, however with some little adjusting, since I'm setting the cookies explicitly in PHP:

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

What this does is check if the cookie is available when the browser is safari. In the next step, we are on the application domain, namely the URI provided as URL_WHERE_APP_IS_LOCATED above.

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

}

So after being redirecting to the application domain, a cookie is set explicitly, and I redirect the user to the authorization process.

In my case (since I'm using CakePHP but it should work fine with any other MVC framework) I'm calling the login action again where the FB authorization is executed another time, and this time it succeeds due to the existing cookie.

After having authorized the app once, I didn't have any more problems using the app with Safari (5.1.6)

Hope that might help anyone.

查看更多
流年柔荑漫光年
7楼-- · 2019-01-01 07:01

I have also been suffering from this problem, but finally got the solution, Initially directly the load the iframe url in browser like small popup then only access the session values inside the iframe.

查看更多
登录 后发表回答