Итак, это уже десятая месть за вопрос «как заставить сторонние файлы cookie работать в Safari», но я спрашиваю снова, потому что думаю, что игровое поле изменилось, возможно, после февраля 2012 года. Один из стандартных трюков для получения третьего партийные файлы cookie в Safari были следующими: использовать некоторый javascript для POST в скрытый iframe. Он (раньше) заставлял Safari думать, что пользователь взаимодействовал со сторонним контентом, а затем разрешать установку файлов cookie.
Я думаю, что эта лазейка была закрыта после небольшого скандала, когда выяснилось, что Google использовал этот трюк со своей рекламой. По крайней мере, при использовании этого трюка я полностью не смог установить файлы cookie в Safari. Я обнаружил несколько случайных сообщений в Интернете, в которых утверждалось, что Apple работает над закрытием лазейки, но я не нашел ни одного официального сообщения.
В качестве запасного варианта я даже попытался переработать основной сторонний фрейм, чтобы вам приходилось нажимать кнопку до загрузки контента, но даже такого уровня прямого взаимодействия было недостаточно, чтобы растопить холодное холодное сердце Safari.
Так кто-нибудь знает наверняка, действительно ли Safari закрыл эту лазейку? Если да, существуют ли другие обходные пути (кроме ручного включения идентификатора сеанса в каждый запрос)?
источник
Ответы:
Просто хотел оставить здесь простое рабочее решение, не требующее взаимодействия с пользователем .
Как я сказал в сообщении, которое я сделал :
По сути, все, что вам нужно сделать, это загрузить свою страницу в 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
источник
Вы сказали, что хотите, чтобы ваши пользователи нажимали кнопку перед загрузкой контента. Мое решение заключалось в том, чтобы кнопка открывала новое окно браузера. Это окно устанавливает файл 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>
источник
Я обманул 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
источник
В вашем контроллере 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
источник
redirect_to params[:return_to]
. Этот параметр необходимо сравнить с белым списком безопасных мест для перенаправления. См. Owasp.org/index.php/…Для моей конкретной ситуации я решил проблему, используя window.postMessage () и исключив любое взаимодействие с пользователем. Обратите внимание, что это будет работать только в том случае, если вы можете каким-то образом выполнить js в родительском окне. Либо включив js из вашего домена, либо если у вас есть прямой доступ к источнику.
В iframe (домен-b) я проверяю наличие файла 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: {} })); }
Затем в родительском окне (домен-а) прослушайте событие.
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
исправить ошибку синтаксисаУ меня была такая же проблема, и сегодня я нашел исправление, которое мне подходит. Если пользовательский агент содержит
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>
Таким образом, пользователь будет снова перенаправлен на вкладку страницы Facebook с уже установленным действительным файлом cookie, и подписанный запрос будет отправлен снова.
источник
В конце концов я выбрал решение, аналогичное тому, которое предоставил Sascha, но с небольшой корректировкой, поскольку я явно устанавливаю файлы 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 выполняется в другой раз, и на этот раз это удается из-за существующего файла cookie.
После авторизации приложения у меня больше не было проблем с использованием приложения с Safari (5.1.6)
Надеюсь, это может кому-нибудь помочь.
источник
У меня была эта проблема на устройствах под управлением iOS. Я сделал магазин, который можно встраивать в обычный веб-сайт с помощью iframe. Каким-то образом при каждой загрузке страницы пользователь получал новый идентификатор сеанса, в результате чего пользователи застревали на полпути процесса, потому что некоторые значения не присутствовали в сеансе.
Я пробовал некоторые решения, приведенные на этой странице, но всплывающие окна не очень хорошо работают на 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.Надеюсь, это кому-то поможет.
источник
Позвольте мне поделиться своим исправлением в 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> } }
источник
Это решение применимо в некоторых случаях - если возможно:
Если страница содержимого iframe использует поддомен страницы, содержащей iframe, файл cookie больше не блокируется.
источник
Google действительно выпустил кошку из мешка на этом. Некоторое время они использовали его для доступа к файлам cookie отслеживания. Почти сразу это было исправлено Apple = \
оригинальный пост Wall Street Journal
источник
Вот код, который я использую. Я обнаружил, что если я устанавливаю какой-либо файл cookie со своего сайта, с этого момента файлы cookie волшебным образом работают в iframe.
http://developocialapps.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(); }
источник
Немного упрощенная версия на 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(); }
источник
Я нашел идеальный ответ на этот вопрос, и все благодаря парню по имени Аллан, который заслуживает здесь всей благодарности. ( 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.
источник
Я использовал модифицированный (добавленный параметр 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']; }
источник
Я также страдал от этой проблемы, но, наконец, получил решение. Первоначально напрямую загружайте URL-адрес iframe в браузере, как небольшое всплывающее окно, а затем обращайтесь только к значениям сеанса внутри iframe.
источник
Safari теперь блокирует все сторонние файлы cookie. Вы можете использовать API хранилища только для того, чтобы попытаться получить доступ пользователей к их сторонним файлам cookie.
https://www.infoq.com/news/2020/04/safari-third-party-cookies-block/
источник
Некоторый контекст, который я не видел четко изложенного в существующих ответах (а также многое изменилось с 2012 года!):
Если вы можете управлять как сторонним iframe, так и родительской страницей (т. Е. Вы можете вставить JavaScript на родительскую страницу), тогда доступно несколько обходных путей. Я бы предположил, что наиболее элегантным из них является использование API postMessage, как описано в ответе @Frank, поскольку а) это не требует перенаправления и б) не требует взаимодействия с пользователем.
Если вы НЕ контролируете как сторонний iframe, так и родительскую страницу , например, у вас есть виджет, размещенный на сайте, который вы не контролируете, то большинство ответов, размещенных здесь, не будут работать в Safari с мая 2020 года и перестанут работать в Chrome около 2022 года . То есть, если пользователь уже не посещал ваш домен или не взаимодействовал с iframe, вы не можете устанавливать файлы cookie. Однако есть некоторые коммерческие службы, предлагающие решения этой проблемы, например CloudCookie.io.
источник
Недавно я столкнулся с той же проблемой в Safari. Решение, которое я придумал, основано на HTML5 API локального хранилища. Используя локальное хранилище, вы можете эмулировать файлы cookie.
Вот мое сообщение в блоге с подробностями: http://log.scalemotion.com/2012/10/how-to-trick-safari-and-set-3rd-party.html
источник
Я решил полностью избавиться от
$_SESSION
переменной и написал обертку вокруг кэша памяти, чтобы имитировать сеанс.Проверьте https://github.com/manpreetssethi/utils/blob/master/Session_manager.php
Пример использования: в тот момент, когда пользователь заходит в приложение, сохраните подписанный запрос с помощью Session_manager, и, поскольку он находится в кеше, вы можете получить к нему доступ на любой странице отныне.
Примечание. Это не будет работать при приватном просмотре в Safari, поскольку session_id сбрасывается каждый раз при перезагрузке страницы. (Глупое сафари)
источник
Вы можете решить эту проблему, добавив заголовок в качестве политики p3p. У меня была такая же проблема на сафари, поэтому после добавления заголовка поверх файлов моя проблема была решена.
<?php header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'); ?>
источник