Safari 3rd Party cookie iframe trick больше не работает?

Итак, это - месть мести "как я могу получить сторонние файлы cookie для работы в Safari", но я снова спрашиваю, потому что, по-моему, игровое поле изменилось, возможно, после февраля 2012 года. Один из стандартных трюков для получения сторонних файлов cookie в Safari было следующее: используйте некоторый javascript для POST для скрытого iframe. Он (привык) обманывал Safari, думая, что пользователь взаимодействовал с третьим сторонним контентом, а затем разрешил устанавливать файлы cookie.

Я думаю, что эта лазейка была закрыта после мягкого скандала, когда выяснилось, что Google использует этот трюк с его рекламой. По крайней мере, используя этот трюк, я полностью не смог установить файлы cookie в Safari. Я обнаружил некоторые случайные интернет-сообщения, в которых утверждалось, что Apple работает над закрытием лазейки, но я не нашел официального слова.

В качестве резерва я даже попробовал перепроектировать основной сторонний фрейм, чтобы вам пришлось нажимать кнопку до загрузки содержимого, но даже этого уровня прямого взаимодействия было недостаточно, чтобы расплавить холодное холодное сердце Safari.

Так кто-нибудь знает наверняка, действительно ли Safari закрыл эту лазейку? Если да, существуют ли другие способы обхода (кроме ручного ввода идентификатора сеанса в каждом запросе)?

Ответ 1

Просто хотел оставить здесь простое рабочее решение, что не требует взаимодействия с пользователем.

Как я заявил в пост, который я сделал:

В основном все, что вам нужно сделать, это загрузить свою страницу на 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, но оно действительно будет работать в любых других подобных ситуациях.


Редактировать 20-дек-2012 - Поддержание подписанного запроса:

Вышеприведенный код не поддерживает отправку данных запроса, и вы потеряете 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

Ответ 2

Вы сказали, что хотите, чтобы ваши пользователи нажимали кнопку до загрузки контента. Мое решение состояло в том, чтобы открыть кнопку нового окна браузера. Это окно устанавливает cookie для моего домена, обновляет открыватель и затем закрывается.

Итак, ваш основной script может выглядеть так:

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

Ответ 3

Я обманул Safari с помощью .htaccess:

#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. Сторонние веб-сайты могли устанавливать файлы cookie, если предпочтение" Блокировать файлы cookie "в Safari было установлено на значение по умолчанию" От третьих лиц и рекламодателей ". http://support.apple.com/kb/HT5190

Ответ 4

В вашем контроллере 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

Ответ 5

В моей конкретной ситуации я решил проблему, используя 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/");
    }

}

Ответ 6

У меня была такая же проблема, и сегодня я нашел исправление, которое отлично работает для меня. Если пользовательский агент содержит 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 Dialog будет перенаправлен на мой URI в верхнем местоположении. Поэтому настройка файлов cookie возможна. Для всех наших приложений холста и вкладки страниц я уже включил следующий script:

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

Таким образом, пользователь будет перенаправлен снова на вкладку страницы Facebook с уже установленным допустимым файлом cookie, и подписанный запрос будет опубликован снова.

Ответ 7

Наконец я пошел на аналогичное решение с тем, которое предоставил Саша, однако с некоторой небольшой настройкой, так как я устанавливаю файлы cookie явно в 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>';

Что это значит, проверьте, доступен ли cookie, когда браузер является сафари. На следующем этапе мы находимся в домене приложения, а именно URI, предоставленный как 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 выполняется в другой раз, и на этот раз ей это удается из-за существующих печенье.

После авторизации приложения один раз у меня не было проблем с приложением с Safari (5.1.6)

Надеюсь, что это поможет кому-нибудь.

Ответ 8

У меня была эта проблема на устройствах под управлением iOS. Я сделал магазин, который встраивается в обычный веб-сайт, используя iframe. Так или иначе, на каждой странице пользователь получил новый sessionid, в результате чего пользователи застряли на полпути, потому что некоторые значения не присутствовали в сеансе.

Я попробовал некоторые из решений, приведенных на этой странице, но всплывающие окна не очень хорошо работают на iPad, и мне нужно было наиболее прозрачное решение.

Я разрешил его с помощью перенаправления. Веб-сайт, который внедряет мой сайт, должен сначала перенаправить пользователя на мой сайт, поэтому верхний кадр содержит URL-адрес моего сайта, где я устанавливаю 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.

Надеюсь, это поможет кому-то.

Ответ 9

Позвольте мне поделиться своим исправлением в 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>
    }
}

Ответ 10

Это решение применяется в некоторых случаях - если возможно:

Если страница содержимого iframe использует субдомен страницы, содержащей iframe, cookie больше не блокируется.

Ответ 11

Google фактически выпустил кошку из сумки на этом. Некоторое время они использовали его для доступа к файлам cookie. Это было исправлено почти сразу Apple =\

оригинал Сообщение Wall Street Journal

Ответ 12

Вот код, который я использую. Я обнаружил, что если я установил любой файл 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();
    }

Ответ 13

Немного простая версия в 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();
}

Ответ 14

Я использовал измененный (добавленный параметр signed_request к ссылке) трюк Whiteagle, и он работал нормально для сафари, но IE постоянно обновляет страницу в этом случае. Поэтому мое решение для сафари и интернет-исследователя:

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

Ответ 15

Я также страдаю от этой проблемы, но, наконец, получил решение. Изначально напрямую загружайте URL-адрес iframe в браузере, например, небольшое всплывающее окно, а затем получаете доступ к значениям сеанса внутри iframe.

Ответ 16

Я нашел отличный ответ на этот вопрос, все благодаря парню по имени Аллан, который заслуживает всякого кредита здесь. (http://www.allannienhuis.com/archives/2013/11/03/blocked-3rd-party-session-cookies-in-iframes/)

Его решение прост и понятен.

На сервере содержимого iframe (домен 2) добавьте файл с именем startession.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.

Ответ 17

Недавно я попал в ту же проблему в Safari. Решение, которое я выяснил, основано на API локального хранилища HTML5. Используя локальное хранилище, вы можете эмулировать файлы cookie.

Здесь мое сообщение в блоге с подробностями: http://log.scalemotion.com/2012/10/how-to-trick-safari-and-set-3rd-party.html

Ответ 18

Я решил избавиться от переменной $_SESSION все вместе и написал обертку вокруг memcache, чтобы имитировать сеанс.

Отметьте https://github.com/manpreetssethi/utils/blob/master/Session_manager.php

Случай использования. Когда пользователь приземляется на приложение, сохраните подписанный запрос с помощью Session_manager и, поскольку он находится в кеше, вы можете получить к нему доступ на любой странице впредь.

Примечание. Это не будет работать при приватном доступе в Safari, поскольку session_id сбрасывается каждый раз при перезагрузке страницы. (Глупые сафари)

Ответ 19

Вы можете решить эту проблему, добавив заголовок в качестве политики p3p. У меня была такая же проблема с сафари, поэтому после добавления заголовка поверх файлов проблема решена.

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