具有基于 HttpOnly cookie 的身份验证和会话管理的单页应用程序 [英] Single page application with HttpOnly cookie-based authentication and session management

查看:30
本文介绍了具有基于 HttpOnly cookie 的身份验证和会话管理的单页应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

几天以来,我一直在为我的单页应用程序寻找一种安全的身份验证和会话管理机制.从大量关于 SPA 和身份验证的教程和博客文章来看,将 JWT 存储在 localStorage 或常规 cookie 中似乎是最常见的方法,但是

For several days now I've been looking for a secure authentication and session management mechanism for my single page application. Judging by the numerous tutorials and blog posts out there about SPAs and authentication, storing JWTs in localStorage or regular cookies seems to be the most common approach, but it's simply not a good idea to use JWTs for sessions so it's not an option for this app.

Requirements

  • Logins should be revokable. For example, if suspicious activity is detected on a user's account, it should be possible to revoke that user's access immediately and require a new log in. Similarly, things like password resets should revoke all existing logins.
  • Authentication should happen over Ajax. No browser redirection ("hard" redirection) should take place afterwards. All navigation should happen inside the SPA.
  • Everything should work cross-domain. The SPA will be on www.domain1.com and the server will be on www.domain2.com.
  • JavaScript should have no access to sensitive data sent by the server, such as session IDs. This is to prevent XSS attacks where malicious scripts can steal the tokens or session IDs from regular cookies, localStorage or sessionStorage.

The ideal mechanism seems to be cookie-based authentication using HttpOnly cookies that contain session IDs. The flow would work like this:

  1. User arrives at a login page and submits their username and password.
  2. The server authenticates the user and sends a session ID as an HttpOnly response cookie.
  3. The SPA then includes this cookie in subsequent XHR requests made to the server. This seems to be possible using the withCredentials option.
  4. When a request is made to a protected endpoint, the server looks for the cookie. If found, it cross-checks that session ID against a database table to make sure the session is still valid.
  5. When the user logs out, the session is deleted from the database. The next time the user arrives on the site, the SPA gets a 401/403 response (since the session has expired), then takes the user to the login screen.

Because the cookie has the HttpOnly flag, JavaScript would not be able to read its contents, so it would be safe from attacks as long as it is transmitted over HTTPS.

Challenges

Here's the specific issue I've run into. My server is configured to handle CORS requests. After authenticating the user, it correctly sends the cookie in the response:

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

However, the browser does not save the cookie (when I check Chrome's local cookies it's not there). So when the following code runs:

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

And the Account page is created:

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

This call does not have the cookie in the header. The server therefore rejects it.

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

Do I need to do something special to make sure the browser saves the cookie after receiving it in the login response? Various sources I've read have said that browsers save response cookies only when the user is redirected, but I don't want any "hard" redirects since this is an SPA.

The SPA in question is written in Vue.js, but I guess it applies to all SPAs. I'm wondering how people handle this scenario.

Other stuff I've read on this topic:

解决方案

I got this working on Vue.js 2 with credentials = true. Setting credentials from client site only half of the story. You need to set response headers from the server as well:

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

You can't use wildcard for Access-Control-Allow-Origin like this:

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

When you specify the credentials: true header, you are required to specify the orgin.

As you can see, this is a PHP code, you can model it to NodeJS or any server side scripting language you are using.

In VueJS I set credentials = true like this in the main.js:

Vue.http.options.credentials = true

In the component, I successfully logged in using 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>

On the server side, things are pretty simple: Login.php on sets a cookie without making any validation whatsoever (Note: this is done for testing purposes only. You are not advised to use this code in production without 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";
}

Finally, the userprofile.php just verifies if a cookie = user is set

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

Successfully logged in

这篇关于具有基于 HttpOnly cookie 的身份验证和会话管理的单页应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆