Application d'une page avec authentification et gestion de session basées sur les cookies HttpOnly

depuis plusieurs jours, je suis à la recherche d'un mécanisme sécurisé d'authentification et de gestion de session pour mon application d'une page. A en juger par les nombreux tutoriels et billets de blog là-bas sur les SPAs et l'authentification, stocker JWTs dans localStorage ou les cookies réguliers semble être l'approche la plus courante, mais ce n'est tout simplement pas une bonne idée d'utiliser JWTs pour les sessions donc ce n'est pas une option pour cette application.

exigences

  • les Logins doivent être révocables. Par exemple, si une activité suspecte est détectée sur le compte d'un utilisateur, il devrait être possible de révoquer l'accès de cet utilisateur immédiatement et nécessitent un nouveau journal. De même, des choses comme les réinitialisations de mot de passe devraient révoquer tous les logins existants.
  • L'authentification
  • devrait se faire par Ajax. Aucune redirection du navigateur (redirection" dure") ne devrait avoir lieu après. Toute navigation doit se faire à l'intérieur du SPA.
  • tout doit fonctionner cross-domain. Le SPA sera sur www.domain1.com et le serveur sera sur www.domain2.com.
  • JavaScript ne doit pas avoir accès aux données sensibles envoyées par le serveur, telles que les identificateurs de session. Ceci est pour prévenir les attaques XSS où des scripts malveillants peuvent voler les tokens ou les ID de session des cookies réguliers, localStorage ou sessionStorage.

le mécanisme idéal semble être basé sur les cookies authentification utilisant les cookies HttpOnly qui contiennent des identificateurs de session. Le flux fonctionnerait comme ceci:

  1. L'utilisateur accède à une page de connexion et soumet son nom d'utilisateur et son mot de passe.
  2. le serveur authentifie l'utilisateur et envoie un identifiant de session comme cookie de réponse HttpOnly .
  3. le SPA inclut alors ce cookie dans les requêtes XHR subséquentes faites au serveur. Cela semble possible en utilisant le withCredentials option.
  4. Lorsqu'une requête est faite vers un terminal protégé, le serveur recherche le cookie. S'il est trouvé, il vérifie que l'ID de session est comparé à une table de base de données pour s'assurer que la session est toujours valide.
  5. Lorsque l'utilisateur se déconnecte, la session est supprimée de la base de données. La prochaine fois que l'utilisateur arrive sur le site, le SPA reçoit une réponse 401/403 (puisque la session a expiré), puis emmène l'utilisateur à l'écran de connexion.

parce que le cookie a le drapeau HttpOnly , JavaScript ne serait pas capable de lire son contenu, il serait donc à l'abri des attaques tant qu'il est transmis par HTTPS.

Défis

voici le problème précis que j'ai rencontré. Mon serveur est configuré pour traiter les requêtes CORS. Après authentification de l'utilisateur, il envoie correctement le cookie dans la réponse:

HTTP/1.1 200 OK
server: Cowboy
date: Wed, 15 Mar 2017 22:35:46 GMT
content-length: 59
set-cookie: _myapp_key=SFMyNTYBbQAAABBn; path=/; HttpOnly
content-type: application/json; charset=utf-8
cache-control: max-age=0, private, must-revalidate
x-request-id: qi2q2rtt7mpi9u9c703tp7idmfg4qs6o
access-control-allow-origin: http://localhost:8080
access-control-expose-headers: 
access-control-allow-credentials: true
vary: Origin

cependant, le navigateur n'enregistre pas le cookie (lorsque je vérifie les cookies locaux de Chrome, ce n'est pas le cas). Ainsi, lorsque le code suivant s'exécute:

context.axios.post(LOGIN_URL, creds).then(response => {
  context.$router.push("/api/account")
}

et la page de compte est créé:

created() {
  this.axios.get(SERVER_URL + "/api/account/", {withCredentials: true}).then(response => {
    //do stuff
  }
}

Cet appel n'a pas le cookie dans l'en-tête. Le serveur le rejette donc.

GET /api/account/ HTTP/1.1
Host: localhost:4000
Connection: keep-alive
Accept: application/json
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8,tr;q=0.6

dois-je faire quelque chose de spécial pour m'assurer que le navigateur sauvegarde le cookie après l'avoir reçu dans la réponse de connexion? Divers des sources que j'ai lues ont dit que les navigateurs enregistrer les cookies de réponse seulement lorsque l'utilisateur est redirigé, mais je ne veux pas de redirections "dures" depuis qu'il s'agit d'un SPA.

la SPA en question est écrite en Vue.js, mais je suppose que ça s'applique à tous les SPAs. Je me demande comment les gens gèrent ce scénario.

autres choses que j'ai lu sur ce sujet:

  • SPA meilleures pratiques pour l'authentification et la gestion de session
  • les Cookies HTTPOnly sont-ils soumis via XmlHTTPRequest avec withCredentials=True?
  • Stop à l'aide de JWT pour des séances, partie 2: Pourquoi votre solution ne fonctionne pas
26
demandé sur Community 2017-03-16 05:49:55

2 réponses

j'ai obtenu ce travail sur Vue.js 2 avec les informations d'identification = true. Établissement des justificatifs d'identité à partir du site client seulement la moitié de l'histoire. Vous devez également définir les en-têtes de réponse à partir du serveur:

    header("Access-Control-Allow-Origin: http://localhost:8080");
    header("Access-Control-Allow-Credentials: true");

vous ne pouvez pas utiliser de Joker pour Access-Control-Allow-Origin comme ceci:

header("Access-Control-Allow-Origin: *");

Lorsque vous spécifiez les informations d'identification: vrai-tête, vous devez spécifier l'origine.

comme vous pouvez le voir, c'est un code PHP, vous pouvez modélisez-le à NodeJS ou n'importe quel langage de script côté serveur que vous utilisez.

Dans VueJS j'définir les informations d'identification = true comme ça dans la main.js:

Vue.http.options.credentials = true

dans le composant, je me suis connecté avec succès en utilisant ajax:

<template>
<div id="userprofile">
    <h2>User profile</h2>
    {{init()}}

</div>
</template>

<script>
    export default {  
        name: 'UserProfile',
        methods: {
        init: function() {


                // loggin in
              console.log('Attempting to login');

            this.$http.get('https://localhost/api/login.php')
            .then(resource =>  {
             // logging response - Notice I don't send any user or password for testing purposes  

            });

            // check the user profile

                console.log('Getting user profile');

                this.$http.get('https://localhost/api/userprofile.php')
                .then(resource =>  {
                    console.log(resource.data);
                })

        }
    }
    }

</script>

Côté Serveur, les choses sont assez simples: Connexion.php on configure un cookie sans aucune validation (Note: Ceci est fait uniquement à des fins de test). Vous n'êtes pas conseillé d'utiliser ce code dans production sans validation)

<?php

header("Access-Control-Allow-Origin: http://localhost:8080");
header("Access-Control-Allow-Credentials: true");

$cookie = setcookie('user', 'student', time()+3600, '/', 'localhost', false , true);

if($cookie){
  echo "Logged in";
}else{
  echo "Can't set a cookie";
}

enfin, le profil utilisateur.php vérifie juste si un cookie = user est défini

<?php

  header("Access-Control-Allow-Origin: http://localhost:8080");
    header("Access-Control-Allow-Credentials: true");


if(isset($_COOKIE['user'])){
  echo "Congratulations the user is ". $_COOKIE['user'];

}else{
  echo "Not allowed to access";
}

S'est connecté avec succès

Successfully logged in

1
répondu Programmer 2017-12-22 04:09:49

avec le réglage des contrôles d'authentification et l'envoi de cookies alors assurez-vous de l'activer lors de l'affichage de connexion afin que le cookie retourné est enregistré dans le navigateur.

0
répondu RDelorier 2017-03-16 21:53:00