Safari сторонний cookie-трюк iframe больше не работает? - PullRequest
132 голосов
/ 29 марта 2012

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

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

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

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

Ответы [ 19 ]

51 голосов
/ 22 августа 2012

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

Как я уже говорил в посте , я сделал :

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

Приведенный выше код не поддерживает данные публикации запросов, и вы потеряете подписанный запрос, если ваше приложение полагается на ощущение подписанного запроса.Вы можете попробовать следующий код:

Примечание. Это все еще проверяется должным образом и может быть менее стабильным, чем первая версия.Используйте на свой страх и риск. Обратная связь приветствуется.

(Спасибо 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
35 голосов
/ 11 апреля 2012

Вы сказали, что хотите, чтобы пользователи нажимали кнопку до загрузки контента.Мое решение состояло в том, чтобы кнопка открывала новое окно браузера.Это окно устанавливает 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 голосов
/ 05 апреля 2012

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

Edit (2012-04-06): Очевидно, Apple «исправила» это с 5.1.4. Я уверен, что это реакция Google: «Существовала проблема с применением политики использования файлов cookie. Сторонние веб-сайты могли устанавливать файлы cookie, если для параметра« Блокировать файлы cookie »в Safari было установлено значение по умолчанию« » От третьих лиц и рекламодателей ". http://support.apple.com/kb/HT5190

14 голосов
/ 07 января 2013

В вашем контроллере 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
12 голосов
/ 15 декабря 2012

Для моей конкретной ситуации я решил проблему с помощью window.postMessage () и исключил любое взаимодействие с пользователем.Обратите внимание, что это будет работать только в том случае, если вы можете каким-либо образом выполнить js в родительском окне.Либо с включением в него js из вашего домена, либо если у вас есть прямой доступ к источнику.

В iframe (domain-b) я проверяю наличие куки, и если он не установлен, будетотправить postMessage родителю (домен-а).Например,

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

Затем в родительском окне (домен-а) прослушайте событие.

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/");
    }

}
9 голосов
/ 31 мая 2012

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

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

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

7 голосов
/ 04 июня 2012

Я наконец-то выбрал решение, подобное тому, которое предоставил Саша, однако с некоторыми небольшими изменениями, так как я устанавливаю куки явно в 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)

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

4 голосов
/ 11 января 2013

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

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

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

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

3 голосов
/ 28 января 2014

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...