web-dev-qa-db-ja.com

シングルページアプリケーションでのAPI認証トークンのストレージ

JSON APIを構築しています。認証には、ランダムトークンベースのアプローチを使用する予定です。ランダムトークンには256ビットのエントロピーがあり、CSRNGを使用して生成されます。トークンのハッシュはデータベースのサーバー側に保存されます。ほとんどのクライアントでは、Authorizationヘッダーで認証トークンを送信します。すべての通信はHTTPSを介して行われます。

私が実装する予定の一般的なパターンは this SO answer の最初の部分で説明されています。

私の質問は、たまたまWebブラウザーで実行されている単一ページのアプリケーションであるAPIのクライアントについてです。私の理解では、XSS攻撃のリスクとJavaScriptの依存関係が危うくなっているため、認証トークンをローカルストレージまたはメモリに保存するのは安全ではありません。 CRSFのリスクがあるため、認証トークンをHttpOnly Cookieに保存するのも安全ではありません。

解決策のようです HttpOnly Cookieでクライアントに認証トークンを送信し、さらに、最初にクライアントでヘッダーに送信される追加のトークンを生成するシンクロナイザートークンパターンを実装します認証する。クライアントはこの追加のトークンをローカルストレージに格納し、後続のリクエストでそれをヘッダーでAPIに返すことができます。

十分に合理的です。しかし、私が理解したいのは、シンクロナイザトークンパターンを実装するための新しい追加トークンを生成しないではなく、元の256ビット認証トークンを分割するだけのリスクがあるかどうかです。クライアントがその半分をHttpOnly Cookieで受け取り、その半分をヘッダーで受け取ってローカルストレージに保持できるようにしますか? APIサーバーは、リクエストを受信すると、完全な認証トークンを再構築します。

確かに、半分の1つが露出した場合、攻撃者が推測できるエントロピーは128ビットしか残っていません(しかし、それでもおそらく十分に難しいと思います)。

そうでなければ、これはセキュリティの観点から本質的に同じことを達成しますか?ここで見逃している微妙な点はありますか?

3
Alex Edwards

JSコードに安全に値を格納する方法を示すための可能な最も簡単な例を次に示します。

var api = (function(){

    let secret = Prompt("enter a secret phrase"); // Only the api object can reach this

    return { // a method to compare guesses to the secret
     auth: phrase=> phrase == secret
    };

}()); // end privacy wrapper

// test it in console by entering bingo at the Prompt:
[api.auth("one"), api.auth("bingo"), api.auth("cheese")];
// evaluates as [false, true, false]

実際のアプリケーションでは、シークレットはajax、Cookie、またはその他の使用するものによってfetch()されます。重要なのは、シークレットへの唯一のアクセスが、「プライベート」変数を定義するラッピング関数内で定義されたメソッドを介することです。このパターンはJSでは "特権メソッド" と呼ばれ、 "api"オブジェクトが規定する契約条件を通じてのみ、外部のコードで利用できるようになります。

1
dandavis