web-dev-qa-db-ja.com

HttpOnly Cookieベースの認証とセッション管理を備えた単一ページアプリケーション

ここ数日、私は自分のシングルページアプリケーション用の安全な認証とセッション管理メカニズムを探していました。 SPAと認証に関する数多くのチュートリアルやブログ投稿から判断すると、localStorageまたは通常のCookieにJWTを保存するのが最も一般的なアプローチのようですが、 セッションにJWTを使用するのは良い考えではありません このアプリのオプションではありません。

要件

  • ログインは取り消し可能にする必要があります。たとえば、ユーザーのアカウントで疑わしいアクティビティが検出された場合、そのユーザーのアクセスをすぐに取り消すことができ、新しいログインが必要になります。同様に、パスワードのリセットなどは既存のすべてのログインを取り消す必要があります。
  • 認証はAjaxを介して行われる必要があります。その後、ブラウザのリダイレクト(「ハード」リダイレクト)を実行しないでください。すべてのナビゲーションはSPA内で行われる必要があります。
  • すべてがクロスドメインで動作するはずです。 SPAはwww.domain1.comにあり、サーバーはwww.domain2.comにあります。
  • JavaScriptは、セッションIDなど、サーバーから送信された機密データにアクセスできないようにする必要があります。これは、悪意のあるスクリプトが通常のcookie、localStorageまたはsessionStorageからトークンまたはセッションIDを盗むことができるXSS攻撃を防ぐためです。

理想的なメカニズムは、セッションIDを含むHttpOnly Cookieを使用したCookieベースの認証と思われます。フローは次のように機能します。

  1. ユーザーがログインページに到着し、ユーザー名とパスワードを送信します。
  2. サーバーはユーザーを認証し、セッションIDをHttpOnly応答Cookieとして送信します。
  3. その後、SPAは、このCookieをサーバーへの後続のXHR要求に含めます。これはwithCredentialsオプションを使用して可能になるようです。
  4. 保護されたエンドポイントに要求が行われると、サーバーはCookieを探します。見つかった場合、そのセッションIDをデータベーステーブルと照合して、セッションがまだ有効であることを確認します。
  5. ユーザーがログアウトすると、セッションはデータベースから削除されます。次にユーザーがサイトに到着すると、SPAは401/403応答を取得し(セッションの有効期限が切れたため)、ユーザーをログイン画面に移動します。

CookieにはHttpOnlyフラグがあるため、JavaScriptはそのコンテンツを読み取ることができず、HTTPSを介して送信される限り、攻撃から安全です。

課題

ここに私が遭遇した特定の問題があります。サーバーは、CORS要求を処理するように構成されています。ユーザーを認証した後、応答でCookieを正しく送信します。

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

ただし、ブラウザはCookieを保存しません(ChromeのローカルCookieをチェックすると、そこにありません)。したがって、次のコードを実行すると:

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

そして、アカウントページが作成されます。

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

この呼び出しのヘッダーにはCookieは含まれません。したがって、サーバーはそれを拒否します。

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

ログイン応答でクッキーを受け取った後、ブラウザがクッキーを保存することを確認するために特別なことをする必要がありますか?私が読んださまざまな情報源は、ブラウザはユーザーがリダイレクトされたときにのみ応答Cookieを保存すると述べていますが、これはSPAなので「ハード」リダイレクトは必要ありません。

問題のSPAはVue.jsで記述されていますが、すべてのSPAに適用されると思います。私は人々がこのシナリオをどのように扱うのか疑問に思っています。

私はこのトピックで読んだ他のもの:

36
Ege Ersoz

私はこれをクレデンシャル= trueでVue.js 2で動作させました。クライアントサイトから資格情報を設定するのは、ストーリーの半分だけです。サーバーからの応答ヘッダーも設定する必要があります。

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

次のように、Access-Control-Allow-Originにワイルドカードを使用することはできません。

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

資格情報:trueヘッダーを指定する場合、orginを指定する必要があります。

ご覧のとおり、これはPHPコードです。NodeJSまたは使用しているサーバー側スクリプト言語に合わせてモデル化できます。

VueJSでは、main.jsで次のように資格情報= trueを設定します。

Vue.http.options.credentials = true

コンポーネントでは、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>

サーバー側では、物事は非常に簡単です:Login.php onは、検証を一切行わずにCookieを設定します(注:これはテスト目的でのみ行われます。検証なしで本番でこのコードを使用することはお勧めしません)

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

最後に、userprofile.phpはcookie = userが設定されているかどうかを確認するだけです

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

3
Programmer

資格情報はCookieの設定と送信を制御するため、ログインに投稿するときに必ず有効にして、返されたCookieがブラウザーに保存されるようにしてください。

0
RDelorier