44 votes

Application à page unique avec authentification HttpOnly et gestion de session basées sur les cookies

Depuis plusieurs jours maintenant, j'ai été à la recherche pour une sécurité d'authentification et de session d'un mécanisme de gestion pour mon single page application. À en juger par les nombreux tutoriels et des articles de blog sur les SPAs et d'authentification, le stockage JWTs dans localStorage ou les témoins réguliers semble être l'approche la plus commune, mais il n'est tout simplement pas une bonne idée d'utiliser JWTs pour les sessions il n'est donc pas une option pour cette application.

Exigences

  • Les connexions devraient être révoquées. 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 dans. De même, des choses comme les réinitialisations de mot de passe doit annuler toutes les connexions.
  • L'authentification devrait arriver au cours de l'Ajax. Pas de redirection du navigateur ("dur" redirection) devrait avoir lieu à la suite. Tous les éléments de navigation doivent se produire à l'intérieur du SPA.
  • Tout devrait fonctionner inter-domaine. Le SPA sera sur www.domain1.com et le serveur sera sur www.domain2.com.
  • JavaScript doit avoir aucun accès à des données sensibles envoyées par le serveur, comme des Identifiants de session. C'est pour empêcher les attaques XSS où des scripts malveillants peuvent voler les jetons ou les Id de session à partir les témoins réguliers, localStorage ou sessionStorage.

Le mécanisme idéal semble être basée sur le cookie d'authentification à l'aide d' HttpOnly les cookies qui contiennent des Identifiants de session. Le flux serait de travailler comme ceci:

  1. L'utilisateur arrive sur une page de login et soumet leur nom d'utilisateur et mot de passe.
  2. Le serveur authentifie l'utilisateur et envoie un ID de session comme un HttpOnly réponse cookie.
  3. Le SPA, puis comprend ce cookie dans la suite XHR requêtes faites au serveur. Cela semble être possible, à l'aide de l' withCredentials option.
  4. Quand une demande est faite à un ordinateur d'extrémité protégé, le serveur recherche le cookie. Si elle est trouvée, il la croix-vérifie que l'ID de session à l'encontre d'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 devient un 401/403 réponse (depuis la session a expiré), puis prend de l'utilisateur à l'écran de connexion.

Parce que le cookie HttpOnly drapeau, JavaScript ne serait pas en mesure de lire son contenu, de sorte qu'il serait à l'abri des attaques tant qu'il est transmis via le protocole HTTPS.

Défis

Voici la question que j'ai pu croiser. Mon serveur est configuré pour gérer les CORS des demandes. Après l'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

Toutefois, le navigateur n'enregistre pas le cookie (quand je vérifie Chrome local de cookies, il n'est pas là). Ainsi, lorsque le code suivant fonctionne:

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

Et la page de votre 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 donc il rejette.

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

Ai-je besoin de faire quelque chose de spécial pour vous assurer que le navigateur enregistre le cookie après la réception de la réponse de connexion? Diverses sources que j'ai lus ont dit que les navigateurs enregistrer la réponse des témoins uniquement lorsque l'utilisateur est redirigé, mais je ne veux pas de "dur" redirige puisque c'est un SPA.

Le SPA en question est écrit en Vue.js mais je suppose que cela s'applique à tous les SPAs. Je me demande comment les gens à gérer ce scénario.

Autres trucs que j'ai lu sur ce sujet:

7voto

Programmer Points 628

J'ai eu ce travail sur Vue.js 2 avec les informations d'identification = true. Définition des informations d'identification de client site seulement la moitié de l'histoire. Vous devez définir des en-têtes de réponse du serveur:

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

Vous ne pouvez pas utiliser des caractères génériques 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 bout de code PHP, vous pouvez le modèle à NodeJS ou de tout langage de script côté serveur que vous utilisez.

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

Vue.http.options.credentials = true

Dans le composant, j'ai réussi à vous connecter 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>

Sur le côté serveur, les choses sont assez simples: Login.php on définit un cookie sans faire aucune validation que ce soit (Note: ceci est fait pour des fins de test uniquement. Vous n'êtes pas conseillé d'utiliser ce code dans la 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, l'userprofile.php juste vérifie si un cookie = l'utilisateur est réglé

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

Connecté avec succès

Successfully logged in

0voto

RDelorier Points 482

Avec les contrôles des informations d'identification, définissant et envoyant les cookies, veillez à l'activer lors de la publication pour vous connecter afin que le cookie renvoyé soit enregistré dans le navigateur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X