web-dev-qa-db-fra.com

Le cookie Safari tiers ne fonctionne plus?

C’est donc la centième revanche de la question "Comment faire en sorte que les cookies tiers fonctionnent dans Safari", mais je le demande à nouveau parce que je pense que les règles du jeu ont changé, peut-être après février 2012. L’un des trucs habituels pour obtenir la 3ème Les cookies de partie dans Safari étaient les suivants: utilisez du javascript pour POST dans un iframe caché. Cela (habituait) à faire croire à Safari que l’utilisateur avait interagi avec le contenu tiers et donc à permettre l’installation de cookies.

Je pense cette échappatoire a été comblée à la suite du doux scandale qui a révélé que Google utilisait ce truc avec ses publicités. À tout le moins, lors de l'utilisation de cette astuce, j'ai été totalement incapable de définir des cookies dans Safari. J'ai découvert des publications Internet aléatoires selon lesquelles Apple travaillait à supprimer cette échappatoire, mais je n'ai trouvé aucun mot officiel.

Comme solution de rechange, j'ai même essayé de redéfinir le cadre principal tiers afin que vous deviez cliquer sur un bouton avant que le contenu ne soit chargé, mais même ce niveau d'interaction directe ne suffisait pas pour faire fondre le cœur froid et froid de Safari.

Alors, est-ce que quelqu'un sait avec certitude si Safari a effectivement comblé cette faille? Si tel est le cas, existe-t-il d'autres solutions (autres que l'inclusion manuelle d'un ID de session dans chaque demande)?

132
gs hurley

Je voulais juste laisser ici une solution de travail simple qui ne nécessite pas d'interaction de l'utilisateur .

Comme je l'ai dit dans un post que j'ai fait :

En gros, tout ce que vous avez à faire est de charger votre page sur top.location, de créer la session et de la rediriger vers facebook.

Ajoutez ce code en haut de votre index.php et définissez $page_url sur l’onglet URL/app finale de votre application et vous verrez que votre application fonctionnera sans problème.

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

Note: Ceci a été fait pour Facebook, mais cela fonctionnerait dans toutes les situations similaires.


Édition 20 décembre 2012 - Gestion de la demande signée:

Le code ci-dessus ne conserve pas les données de publication des demandes et vous perdriez la demande signée. Si votre application repose sur une demande signée, n'hésitez pas à essayer le code suivant:

Remarque: cette option est toujours testée correctement et peut être moins stable que la première version. Utilisez à vos risques et périls/Vos commentaires sont appréciés.

(Merci à CBroe pour m'avoir orienté dans la bonne direction permettant ainsi d'améliorer la solution)

// 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
51
Diogo Raminhos

Vous avez indiqué que vous souhaitiez que vos utilisateurs cliquent sur un bouton avant le chargement du contenu. Ma solution consistait à ouvrir un bouton dans une nouvelle fenêtre du navigateur. Cette fenêtre définit un cookie pour mon domaine, actualise l’ouvreur puis se ferme.

Donc, votre script principal pourrait ressembler à:

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

Alors safari_cookie_fix.php se présente comme suit:

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

J'ai trompé Safari avec un .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>

Et ça a cessé de fonctionner pour moi aussi. Toutes mes applications perdent la session dans Safari et redirigent leurs comptes vers Facebook. Comme je suis pressé de réparer ces applications, je suis actuellement à la recherche d'une solution. Je te tiendrai au courant.

Modifier (2012-04-06): Apparemment, Apple l'a "corrigé" avec 5.1.4. Je suis sûr que c'est la réaction à Google-chose: "Un problème existait dans l'application de sa politique de cookie. Les sites Web tiers pourraient définir des cookies si la préférence" Bloquer les cookies "dans Safari était définie sur le paramètre par défaut" De tiers et annonceurs ". http://support.Apple.com/kb/HT519

15
vwoelm

Dans votre contrôleur Ruby sur Rails, vous pouvez utiliser:

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
14
joost

Pour ma situation spécifique, j'ai résolu le problème en utilisant window.postMessage () et en éliminant toute interaction de l'utilisateur. Notez que cela ne fonctionnera que si vous pouvez en quelque sorte exécuter js dans la fenêtre parente. Soit en y incluant un js de votre domaine, soit en ayant un accès direct à la source.

Dans l'iframe (domaine-b), je vérifie la présence d'un cookie et, s'il n'est pas défini, j'enverrai un postMessage au parent (domaine-a). Par exemple;

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

Ensuite, dans la fenêtre parente (domaine-a), écoutez l'événement.

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;
    }
}

Enfin, sur votre serveur (http://www.domain-b.com/safari/cookiefix), vous définissez le cookie et redirigez-le vers l’origine de l’utilisateur. L'exemple ci-dessous utilise 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/");
    }

}
12
Frank

J'ai eu le même problème et aujourd'hui j'ai trouvé un correctif qui fonctionne bien pour moi. Si l'agent utilisateur contient Safari et qu'aucun cookie n'est défini, je redirige l'utilisateur vers la boîte de dialogue 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 } ?>

Après l'authentification et la demande des autorisations, la boîte de dialogue OAuth sera redirigée vers mon URI situé à l'emplacement le plus élevé. Donc, installer des cookies est possible. Pour toutes nos applications de canevas et d'onglets de page, j'ai déjà inclus le script suivant:

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

L'utilisateur sera donc redirigé vers l'onglet de la page Facebook avec un cookie valide déjà défini et la demande signée sera à nouveau publiée.

9
Sascha Galley

J'ai finalement opté pour une solution similaire à celle fournie par Sascha, avec toutefois quelques ajustements mineurs, car je mets explicitement les cookies en 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>';

Cela permet de vérifier si le cookie est disponible lorsque le navigateur est safari. Dans l'étape suivante, nous nous trouvons dans le domaine d'application, à savoir l'URI indiqué ci-dessus sous la forme 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

}

Ainsi, après avoir été redirigé vers le domaine d'application, un cookie est défini explicitement et je redirige l'utilisateur vers le processus d'autorisation.

Dans mon cas (puisque j'utilise CakePHP mais cela devrait fonctionner correctement avec tout autre framework MVC), j'appelle l'action de connexion à nouveau lorsque l'autorisation du FB est exécutée une autre fois, et cette fois, elle réussit grâce au cookie existant.

Après avoir autorisé l'application une fois, je n'ai plus eu de problèmes pour utiliser l'application avec Safari (5.1.6).

J'espère que cela pourrait aider n'importe qui.

7
Marcel Kalveram

J'ai eu ce problème sur les appareils sous iOS. J'ai créé une boutique intégrable dans un site Web normal à l'aide d'un iframe. D'une manière ou d'une autre, à chaque chargement de page, l'utilisateur obtenait un nouvel identifiant de session, ce qui les bloquait à mi-chemin du processus car certaines valeurs n'étaient pas présentes dans la session.

J'ai essayé certaines des solutions données sur cette page, mais les popups ne fonctionnent pas très bien sur un iPad et j'avais besoin de la solution la plus transparente.

Je l'ai résolu en utilisant une redirection. Le site Web qui intègre mon site doit d'abord rediriger l'utilisateur vers mon site. Le cadre top contient l'URL de mon site, dans lequel je mets un cookie et redirige l'utilisateur vers la page appropriée sur le site Web qui intègre mon site, qui est transmise dans l'URL.

Exemple PHP code

Le site Web distant redirige l'utilisateur vers

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;

L'utilisateur se retrouve sur http://www.domain.com/shop/frame où mon site est intégré, stocke les sessions comme il se doit et consomme des cookies.

J'espère que ça aide quelqu'un.

4
ar34z

Permettez-moi de partager mon correctif dans ASP.NET MVC 4. L’idée principale ressemble à une réponse correcte pour PHP. Le prochain code ajouté dans la disposition principale dans l'en-tête près de la section des scripts:

@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
Yara

Cette solution s'applique dans certains cas - si possible:

Si la page de contenu iframe utilise un sous-domaine de la page contenant l'iframe, le cookie n'est plus bloqué.

3
eye-wonder

Voici un code que j'utilise. J'ai constaté que si je mets un cookie sur mon site, les cookies fonctionnent comme par magie dans l'iframe à partir de ce moment-là.

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();
    }
1
Tom Kincaid

Google a en fait laissé le chat sortir du sac avec celui-ci. Ils l'utilisaient depuis un moment pour accéder aux cookies de suivi. Il a été corrigé presque immédiatement par Apple = \

original article du Wall Street Journal

1
Colin Godsey

Une version légèrement plus simple en PHP de ce que d'autres ont posté:

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
Charlie Schliesser

J'ai trouvé la réponse parfaite à tout cela, tout cela grâce à un gars appelé Allan qui mérite tout le mérite ici. ( http://www.allannienhuis.com/archives/2013/11/03/blocked-3rd-party-session-cookies-in-iframes/ )

Sa solution est simple et facile à comprendre.

Sur le serveur de contenu iframe (domaine 2), ajoutez un fichier appelé startsession.php au niveau du domaine racine, qui contient:

<?php
// startsession.php
session_start();
$_SESSION['ensure_session'] = true;
die(header('location: '.$_GET['return']));

Maintenant sur le site web de premier niveau contenant l'iframe (domain1), l'appel à la page contenant l'iframe devrait ressembler à ceci:

<a href="https://domain2/startsession.php?return=http://domain1/pageWithiFrame.html">page with iFrame</a>

Et c'est tout! Simples :)

Cela fonctionne parce que vous dirigez le navigateur vers une URL tierce et lui dites donc de lui faire confiance avant d'afficher le contenu de celle-ci dans l'iframe.

1
user1351772

J'ai utilisé l'astuce modifiée de Whiteagle (cela a fonctionné correctement pour safari, mais IE actualise constamment la page dans ce cas. Donc, ma solution pour Safari et Internet Explorer est la suivante:

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

J'ai également souffert de ce problème, mais j'ai finalement trouvé la solution. Initialement, chargez directement l'url de l'iframe dans le navigateur, comme un petit menu contextuel, puis accédez uniquement aux valeurs de session à l'intérieur de l'iframe.

0
Karthik Keyan