これは「SafariでサードパーティCookieを使用するにはどうすればよいですか」という質問の復です。しかし、おそらく2012年2月以降に競技場が変わったと思うので、もう一度質問します。 SafariのパーティCookieは次のとおりです。JavaScriptを使用して、非表示のiframeにPOSTします。 Safariが(ユーザーがサードパーティのコンテンツを操作したと考えて、Cookieの設定を許可するように)使用します。
I 思考この抜け穴は、Googleが広告でそのトリックを使用していることが明らかになった軽度のスキャンダルをきっかけに閉じられました。少なくとも、このトリックを使用している間、SafariでCookieを完全に設定することはできませんでした。 Appleが抜け穴の閉鎖に取り組んでいると主張するランダムなインターネット投稿を発掘しましたが、公式の言葉は見つかりませんでした。
フォールバックとして、コンテンツを読み込む前にボタンをクリックする必要があるようにメインサードパーティフレームを再設計することも試みましたが、そのレベルの直接的な対話でさえ、Safariの冷たい冷たい心を溶かすには十分ではありませんでした。
だから、Safariが本当にこの抜け穴を塞いだかどうか、誰もが確実に知っていますか?その場合、他の回避策はありますか(すべてのリクエストにセッションIDを手動で含めること以外)?
ここでは、ユーザーの操作を必要としない簡単な作業ソリューションを残したかったです。
投稿した で述べたように:
基本的に必要なことは、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用に作成されたものですが、実際には他の同様の状況で機能します。
上記のコードは、データを送信した後のリクエストを保持しません。アプリケーションが署名済みのリクエストに依存している場合、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でwithしました:
#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ポリシーの実施に問題がありました。Safariの「Cookieをブロック」設定がデフォルト設定の場合、サードパーティのWebサイトはCookieを設定できます。サードパーティと広告主から」。 http://support.Apple.com/kb/HT519
Ruby上の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
特定の状況では、window.postMessage()を使用して、ユーザーの操作を排除することで問題を解決しました。これは、親ウィンドウでjsを何らかの方法で実行できる場合にのみ機能することに注意してください。ドメインのjsを含めるか、ソースに直接アクセスできる場合。
Iframe(domain-b)でcookieの存在を確認し、設定されていない場合、親(domain-a)に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: {} }));
}
次に、親ウィンドウ(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/");
}
}
私は同じ問題を抱えていましたが、今日は私にとっては問題なく動作する修正を見つけました。ユーザーエージェントに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>
そのため、ユーザーは有効なCookieが既に設定されているでFacebookページタブに再度リダイレクトされ、署名されたリクエストが再度投稿されます。
私は最終的にSaschaが提供したものと同様のソリューションを探しましたが、PHPでCookieを明示的に設定しているため、少し調整が必要です。
// 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が利用可能かどうかを確認します。次のステップでは、アプリケーションドメイン、つまり上記のURL_WHERE_APP_IS_LOCATEDとして提供されたURIを使用します。
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により成功します。
アプリを1回認証した後、Safari(5.1.6)でアプリを使用しても問題はありませんでした
それが誰にも役立つことを願っています。
IOSを実行しているデバイスでこの問題が発生しました。 iframeを使用して、通常のWebサイトに埋め込み可能なショップを作成しました。どういうわけか、ページロードごとにユーザーは新しいセッションIDを取得し、その結果、セッションに値が存在しなかったためにプロセスの途中でユーザーがスタックしてしまいます。
このページにあるソリューションのいくつかを試しましたが、iPadではポップアップがうまく機能しないため、最も透明なソリューションが必要でした。
リダイレクトを使用して解決しました。サイトを埋め込むWebサイトは、最初にユーザーをサイトにリダイレクトする必要があるため、topフレームにはサイトへのURLが含まれ、Cookieを設定し、私のサイトを埋め込むWebサイトの適切なページにユーザーをリダイレクトします。これはURLで渡されます。
例PHPコード
リモートWebサイトはユーザーを
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はブロックされなくなります。
ここに私が使用するいくつかのコードがあります。自分のサイトからCookieを設定すると、それ以降iframeでCookieが魔法のように機能することがわかりました。
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();
}
グーグルは実際にこの猫を猫の袋から出しました。しばらくの間、トラッキングCookieにアクセスするために使用していました。 Apple = \によってほぼすぐに修正されました
オリジナル Wall Street Journalの投稿
他の人が投稿したものの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)を含むトップレベルのWebサイトで、iframeを含むページへの呼び出しは次のようになります。
<a href="https://domain2/startsession.php?return=http://domain1/pageWithiFrame.html">page with iFrame</a>
以上です!シンプル:)
これが機能する理由は、ブラウザをサードパーティのURLに誘導し、iframe内のコンテンツを表示する前にブラウザに信頼するように指示しているためです。
変更(signed_requestパラメーターをリンクに追加)Whiteagleのトリックを使用し、safariでは問題なく動作しましたが、その場合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'];
}
私もこの問題に苦しんでいましたが、最終的に解決策を得ました。最初は小さなポップアップのようにブラウザで直接iframe urlをロードし、iframe内のセッション値にのみアクセスします。