web-dev-qa-db-ja.com

RESTfulバックエンドを使用したJavaScriptシングルページアプリの保護

私は現在JavaScript SPAを構築中であり、それを保護する方法を研究しています。現在、AJAXを介して完全に相互作用しているRESTful APIがあります。このAPIと対話するモバイルクライアントもあり、現在、SSLを介したHTTP BASIC認証のみをサポートしています。 JavaScriptアプリもSSL経由でのみ通信しますが、クライアントにパスワード(またはその派生物)を保存する必要があるため、BASIC Authはそれをカットしません。最後に、SPAアプリは純粋なJavaScriptとHTMLで、RESTful APIと同じサーバーで提供されますが、サーバー側のフレームワークはありません。

目標

  • JavaScriptクライアント用のサーバー側フレームワークはありません(それは単なる別のクライアントです)。
  • 典型的な理由(スケーラビリティ、フォールトトレランス、導入の簡素化など)のために、RESTful APIのステートレス性を維持する
  • すべての状態はクライアントによって維持される必要があります。この質問では、これはログイン資格情報を意味します。
  • クライアントによって維持されるログイン状態は、安全で、セッションハイジャックや類似の攻撃に対して耐性がなければなりません。

私が思いついたのは、OAuthと同様のスキーム(Amazonなど))の私の研究に基づいています。

  1. ユーザーは、SSL経由でHTTP POSTを使用してログインします。
  2. サーバーは次のようにハッシュを計算します。

    HMAC(key、userId + ":" + ipAddress + ":" + userAgent + ":" + todaysDateInMilliseconds)

  3. このトークンはクライアントに返され、userNameとpasswordの代わりに後続のすべてのリクエストで提供されます。ほとんどの場合、localStorageまたはcookieに保存されます。

これは安全ですか? userId、ipAddress、todaysDateInMillesecondsを選択する私の動機は、今日のみ有効なトークンを作成することですが、すべてのリクエストに対してデータベース検索を必要とせず、クライアントに安全に保存できます。キーが危険にさらされることはないので、セッションハイジャックを防ぐためにIPアドレスを含めることはできません。

StackExchangeの関連する投稿からの次のリンクを含めてみましょう。解決しようとしている多くの問題に対処していると思います。 RESTおよびステートレスセッションID

ここでの最初のフィードバックの後、プロキシの背後のクライアントとモバイルクライアントをより適切に処理するために、IPアドレスの最初の2つのオクテットのみを使用することにしました。それはまだ完璧ではありませんが、追加のセキュリティとのトレードオフです。

68
Jon Wingfield

トークンによって提供されるサービスは、サーバーが何らかの形でトークンを独自のものとして認識することです。サーバーはどのようにしてHMACベースのトークンを検証できますか?秘密のHAMCキーとHMACが動作するデータを使用して再計算します。トークンをユーザーID、パスワード、IP、および日付に基づいて計算する場合、サーバーはそのすべての情報を知っている必要があります。ただし、サーバーにパスワードを保存する必要はなく、クライアントは要求ごとにパスワードを送り返しません。では、システムはどのように機能しますか?

ただし、基本的な考え方は健全です。

  • ユーザーは、適切と思われる方法で「ログイン」します。
  • このようなログイン時に、サーバーはCookie値を送信し、後続の各リクエストでそれを送り返します(これがCookieが行うことです)。
  • Cookieには、ユーザーID、発行日、および値m = HMAC(K、userID || date || IP)が含まれています。
  • サーバーはリクエストを受信すると、Cookieを検証します。ユーザーIDと日付はCookie自体から取得され、ソースIPはWebサーバーレイヤーから取得され、サーバーは値を再計算できますm Cookieに保存されているものと一致することを確認します。

サーバーに(一時的な)記憶域がある場合は、Cookie全体をランダムなセッションIDに置き換えることができます。実際、サーバーはランダムなセッションIDからユーザー固有の情報(名前やIPアドレスなど)へのマッピングを記憶できます。古いセッションIDは自動的に期限切れになる可能性があるため、ストレージスペースが無制限に大きくなることはありません。上記のcookieは、クライアント自体でoffload storageを行うための単なる方法です。

注: IPアドレスを使用すると、実際的な問題が発生する可能性があります。一部のクライアントはプロキシの背後にあり、負荷分散されたプロキシでさえあるため、クライアントのIPアドレスが(サーバーから)非表示になっているだけでなく(クライアントのアドレスではなく、プロキシのアドレスが表示されます)、サーバー側で取得したIPアドレスが不規則に移動します(クライアントからの2つの連続した要求がプロキシファーム内の個別のプロキシを通過した場合)。

30
Thomas Pornin

より簡単な解決策があります:

サイト全体でSSLを使用してから、フレームワークの標準セッショントラッキングを使用します。

それがあなたがする必要があるすべてです。

詳細には、ユーザーは最初にユーザー名とパスワードを入力してログインします。有効性を確認できるサーバーにPOSTされます。認証が成功すると、サーバーコードはセッション状態にフラグを設定し、ユーザーが認証に成功したことと、ユーザーのユーザー名を記憶します。後続のすべてのリクエストは同じセッションを経由するため、簡単に認証して続行することができます。

(さらに詳しく:リクエストを受け取るたびに、セッション状態をチェックして、ユーザーが正常に認証され、このアクションを実行する権限があるかどうかを確認します。はいの場合、リクエストを許可してアクションを実行します。いいえの場合、リダイレクトしますユーザーをログインページに表示するか、その他のエラーメッセージを表示します。)

これはすべての要件を満たしていることに注意してください。リクエストごとにデータベースを検索する必要はありません。 RESTful APIと互換性があります。シングルページのアプリと互換性があります。特別なコードを作成する必要はありません。フレームワークの既存のセッションサポートを使用するだけです(通常、一意のセッションIDを持つセッションCookieと、そのセッションIDに関連付けられたサーバー側の状態を保存するメカニズムを使用します)。 。

サイト全体でSSLを使用する一環として、セッションID Cookieにsecureフラグを設定して、盗聴から保護する必要があります(これにより、HTTP経由で送信されないことが保証されます)。また、HSTSを有効にして、サイトで常にSSLを使用するようブラウザに指示する必要があります。 SSLをサイト全体に展開する方法の詳細については、このサイトを検索してください。

クライアントのIPアドレスが静的であることに依存しないでください。たとえば、クライアントがモバイルであり、あるワイヤレスネットワークから別のワイヤレスネットワークに移動した場合など、状況が変わることがあります。したがって、クライアントのIPアドレスを使用しないことをお勧めします。

9
D.W.

この投稿をチェックしてください HTML5 WebアプリでのOAuth2の使用 。 @jandersenの回答は、単一ページアプリケーションでの リソース所有者のパスワード資格情報 フローの使用についての適切な説明を提供します。いずれの場合も、このアプリの推奨フローは Implicit grant です。実際、参照された投稿に対する@jandersenの応答は、暗黙的な付与に近いものとして機能するようにリソース所有者のパスワード資格情報を微調整することに関するものです。

3
Daniel Cerecedo