首页 文章

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

提问于
浏览
131

所以这是“我如何让第三方cookie在Safari中工作”的第十五次复仇,但我再次提问,因为我认为比赛场地已经改变,可能是在2012年2月之后 . 获得第三名的标准技巧之一Safari中的派对cookie如下:使用一些javascript POST到隐藏的iframe . 它(曾经)欺骗Safari认为用户已经与第三方内容进行了交互,因此允许设置cookie .

我认为这个漏洞已经在温和的丑闻之后被关闭,据透露,谷歌正在利用其广告中的这一技巧 . 至少,在使用这个技巧时,我完全无法在Safari中设置cookie . 我发现了一些随机的互联网帖子,声称Apple正在努力弥补漏洞,但我还没有发现任何官方消息 .

作为后备,我甚至尝试重新设计主要的第三方框架,以便在内容加载之前必须单击按钮,但即使是这种级别的直接交互也不足以融化Safari冷酷的心脏 .

那么有人确切知道Safari是否确实关闭了这个漏洞?如果是,是否有其他解决方法(除了在每个请求中手动包含会话ID)?

19 回答

  • 51

    只想在这里留下一个简单的工作解决方案 does not require user interaction .

    正如我在_3004945中所述:

    基本上你需要做的就是将你的页面加载到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,请尝试以下代码:

    Note: This is still being tested properly and may be less stable than the first version. Use at your own risk / Feedback is appreciated.

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

    您说您愿意让用户在内容加载前单击按钮 . 我的解决方案是让一个按钮打开一个新的浏览器窗口 . 该窗口为我的域设置了一个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>
    
  • 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 "fixed"与5.1.4 . 我确定这是对Google事物的反应:"An issue existed in the enforcement of its cookie policy. Third-party websites could set cookies if the "阻止Cookie " preference in Safari was set to the default setting of "来自第三方和广告客户“.http://support.apple.com/kb/HT5190

  • 1

    在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
    
  • 7

    对于我的具体情况,我通过使用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并重定向回用户来自的位置 . 下面的例子是使用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/");
        }
    
    }
    
  • 4

    我有同样的问题,今天我找到了一个适合我的修复方法 . 如果用户代理包含 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>
    

    因此,用户将再次被重定向到带有 valid cookie already set 的Facebook页面选项卡,并再次发布已签名的请求 .

  • -1

    我终于找到了一个类似于Sascha提供的解决方案的解决方案,但稍微调整一下,因为我明确地设置了cookiePHP:

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

    因此,在重定向到应用程序域后,会明确设置cookie,并将用户重定向到授权过程 .

    在我的情况下(因为我正在使用CakePHP,但它应该适用于任何其他MVC框架)我再次调用登录操作,其中FB授权再次执行,这次它由于现有cookie而成功 .

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

    希望可以帮助任何人 .

  • 14

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

    我尝试了本页面给出的一些解决方案,但弹出窗口在iPad上运行效果不佳,我需要最透明的解决方案 .

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

    Example PHP code

    远程网站将用户重定向到

    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 ,存储会话和吃 Cookies .

    希望这有助于某人 .

  • -1

    让我在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>
        }
    }
    
  • 1

    此解决方案适用于某些情况 - 如果可能:

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

  • 0

    谷歌实际上让这只猫从包里拿出来了 . 他们使用它一段时间来访问跟踪cookie . 苹果公司几乎立即修复它

    原始Wall Street Journal post

  • 3

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

    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

    我找到了一个完美的答案,这一切都要归功于一个名叫艾伦的家伙,这值得所有的荣誉 . (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中显示内容之前信任它 .

  • 3

    我使用了修改(添加了signed_request param到链接)Whiteagle的技巧,它适用于safari,但IE在这种情况下不断刷新页面 . 所以我的safari和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'];
    }
    
  • 9

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

  • 35

    我最近在Safari上遇到了同样的问题 . 我发现的解决方案基于Local Storage HTML5 API . 使用本地存储,您可以模拟cookie .

    这是我的博文,内容详情如下:http://log.scalemotion.com/2012/10/how-to-trick-safari-and-set-3rd-party.html

  • 0

    我决定一起摆脱 $_SESSION 变量并在memcache周围写一个包装来模仿会话 .

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

    用例:当用户登陆应用程序时,使用Session_manager存储已签名的请求,因为它在缓存中,您可以在此后的任何页面上访问它 .

    注意:这在Safari中私下浏览时不起作用,因为每次页面重新加载时session_id都会重置 . (愚蠢的野生动物园)

  • 1

    你可以通过添加 Headers 来解决这个问题,因为p3p policy..i在safari上有相同的问题,所以在文件顶部添加 Headers 后解决了我的问题 .

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

相关问题