web-dev-qa-db-ja.com

認証およびセッション管理に関するSPAのベストプラクティス

Angular、Ember、Reactなどのフレームワークを使用してSPAスタイルのアプリケーションを構築するとき、人々は認証およびセッション管理のためのベストプラクティスであると思いますか?問題への取り組みを検討する方法はいくつか考えられます。

  1. APIとUIに同じOriginドメインがあると仮定して、通常のWebアプリケーションでの認証と同じように扱います。

    これにはおそらくセッションCookie、サーバー側のセッションストレージ、そしておそらく認証されたWeb UIがパーソナライズやクライアント側のロール/能力の決定に役立つように現在のユーザー情報を取得するためにヒットできるセッションAPIエンドポイントがあります。サーバーはもちろんデータへのアクセスを保護するルールを強制します。UIはこの情報を使用してエクスペリエンスをカスタマイズします。

  2. パブリックAPIを使用している他のサードパーティ製クライアントと同様に扱い、OAuthと同様の何らかのトークンシステムで認証します。このトークンメカニズムは、クライアントAPIがサーバーAPIに対して行われたすべての要求を認証するために使用されます。

私はここではあまり専門家ではありませんが、#1で大部分のケースで完全に十分であるように思えます、しかし私は本当にいくつかのより経験豊富な意見を聞きたいです。

263
Chris Nicola

この質問は、やや異なる形式で、長々とここで対処されています。

RESTful認証

ただし、これはサーバー側から対処します。これをクライアント側から見てみましょう。ただし、その前に、重要なプレリュードがあります。

Javascript Cryptoは絶望的です

これに関するMatasanoの記事は有名ですが、そこに含まれるレッスンは非常に重要です。

http://www.matasano.com/articles/javascript-cryptography/

要約する:

  • 中間者攻撃は、暗号コードを<script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>で簡単に置き換えることができます
  • 中間者攻撃は、非SSL接続を介して任意のリソースを提供するページに対して簡単です。
  • SSLを取得したら、とにかく本物の暗号を使用しています。

そして、独自の結果を追加するには:

  • XSS攻撃が成功すると、SSLを使用している場合でも、攻撃者がクライアントのブラウザーでコードを実行する可能性があります-したがって、すべてのハッチをバテンダウンしたとしても、攻撃者が実行する方法を見つけた場合、ブラウザーの暗号は失敗する可能性があります他の誰かのブラウザ上のjavascriptコード。

これにより、JavaScriptクライアントを使用する場合、多くのRESTful認証スキームが不可能または愚かになります。見てみよう!

HTTP基本認証

何よりもまず、HTTP Basic Auth。最も単純なスキーム:リクエストごとに名前とパスワードを渡すだけです。

もちろん、これにはSSLが絶対に必要です。なぜなら、リクエストごとにBase64(可逆)エンコードされた名前とパスワードを渡すからです。回線を聞いている人はだれでも、ユーザー名とパスワードを簡単に抽出できます。 「Basic Auth is insecure」引数のほとんどは、ひどい考えである「Basic Auth over HTTP」の場所から来ています。

ブラウザはベイクインされたHTTP Basic Authサポートを提供しますが、それは罪として見苦しく、おそらくあなたのアプリには使用すべきではありません。ただし、JavaScriptでユーザー名とパスワードを隠しておく方法もあります。

これが最もRESTfulなソリューションです。サーバーは状態に関する知識を一切必要とせず、ユーザーとの個々のやり取りをすべて認証します。一部のREST愛好家(主にストローマン)は、何らかの状態を維持することは異端であり、他の認証方法を考えると口から泡立つと主張します。この種の標準準拠には理論上の利点があります-すぐに使えるApacheでサポートされています-あなたの心が望むなら、オブジェクトを.htaccessファイルで保護されたフォルダーにファイルとして保存できます!

問題?クライアント側でユーザー名とパスワードをキャッシュしています。これにより、evil.ruにより良いクラックが与えられます。最も基本的なXSS脆弱性でさえ、クライアントがユーザー名とパスワードを悪意のあるサーバーに送信する可能性があります。パスワードをハッシュおよびソルトすることでこのリスクを軽減することもできますが、覚えておいてください:JavaScript CryptoはHopelessです。ブラウザの基本認証サポートに任せることでこのリスクを軽減することができますが、前述のように、罪としてはasいです。

HTTPダイジェスト認証

jQueryでダイジェスト認証は可能ですか?

より「安全な」認証、これはリクエスト/レスポンスハッシュチャレンジです。 JavaScript CryptoはHopelessを除き、SSLでのみ機能し、クライアント側でユーザー名とパスワードをキャッシュする必要があるため、より複雑になりますHTTP基本認証、ただしこれ以上安全ではありません

追加の署名パラメーターを使用したクエリ認証。

もう1つのより「安全な」認証。パラメータをノンスとタイミングデータで暗号化し(繰り返し攻撃とタイミング攻撃から保護するため)、送信します。これの最良の例の1つはOAuth 1.0プロトコルです。これは、私が知る限り、RESTサーバーで認証を実装するためのかなり頑固な方法です。

http://tools.ietf.org/html/rfc5849

ああ、でもJavaScript用のOAuth 1.0クライアントはありません。どうして?

JavaScript Cryptoは絶望的です、覚えておいてください。 JavaScriptはSSLなしでOAuth 1.0に参加できず、クライアントのユーザー名とパスワードをローカルに保存する必要があります-これはダイジェスト認証と同じカテゴリーに入れます-HTTP基本認証よりも複雑ですが、 これ以上安全ではありません

トークン

ユーザーはユーザー名とパスワードを送信し、代わりにリクエストの認証に使用できるトークンを取得します。

これは、ユーザー名/パスワードトランザクションが完了するとすぐに機密データを破棄できるため、HTTP基本認証よりもわずかに安全です。また、トークンは「状態」を構成し、サーバーの実装をより複雑にするため、RESTfulでもありません。

まだSSL

ただし、トークンを取得するには、その初期ユーザー名とパスワードを送信する必要があります。機密情報は、侵害される可能性のあるJavaScriptに影響を与えます。

ユーザーの資格情報を保護するには、攻撃者がJavaScriptにアクセスできないようにする必要があり、ユーザー名とパスワードをネットワーク経由で送信する必要があります。 SSLが必要です。

トークンの有効期限

「ねえ、このトークンが長すぎた場合は破棄して、ユーザーを再度認証させる」などのトークンポリシーを適用するのが一般的です。または「このトークンの使用が許可されている唯一のIPアドレスはXXX.XXX.XXX.XXXであると確信しています。」これらのポリシーの多くは、非常に優れたアイデアです。

ファイアシーピング

ただし、SSLなしでトークンを使用すると、「サイドジャッキング」と呼ばれる攻撃に対して依然として脆弱です。 http://codebutler.github.io/firesheep/

攻撃者はユーザーの資格情報を取得しませんが、ユーザーのふりをすることはできますが、これはかなり悪い場合があります。

tl; dr:暗号化されていないトークンをネットワーク経由で送信することは、攻撃者がそれらのトークンを簡単に奪い、ユーザーのふりをすることができることを意味します。 FireSheepは、これを非常に簡単にするプログラムです。

独立した、より安全なゾーン

実行しているアプリケーションが大きいほど、機密データの処理方法を変更するコードをインジェクトでき​​ないようにすることは絶対に難しくなります。 CDNを絶対に信頼していますか?広告主ですか?あなた自身のコードベース?

クレジットカードの詳細には一般的であり、ユーザー名とパスワードにはあまり一般的ではありません-一部の実装者は、「機密データ入力」をアプリケーションの残りの部分とは別のページ、厳しく制御および可能な限り最適にロックダウンできるページに保持しますユーザーをフィッシングするのは困難です。

Cookie(トークンを意味します)

認証トークンをCookieに入れることは可能です(そして一般的です)。これは、トークンを使用したauthのプロパティを変更するものではなく、より便利なものです。前述の引数はすべて適用されます。

セッション(まだトークンを意味します)

セッション認証は単なるトークン認証ですが、若干の違いがあるため、若干異なるもののように見えます。

  • ユーザーは、認証されていないトークンで始まります。
  • バックエンドは、ユーザーのトークンに関連付けられた「状態」オブジェクトを保持します。
  • トークンはCookieで提供されます。
  • アプリケーション環境により、詳細が抽象化されます。

ただし、それを除けば、実際にはトークン認証と違いはありません。

これは、RESTful実装からさらに遠ざかります。ステートオブジェクトを使用すると、ステートフルサーバー上のプレーンol 'RPCのパスをさらに進んでいきます。

OAuth 2.0

OAuth 2.0は、「ソフトウェアBがユーザーXのログイン資格情報にアクセスすることなく、ソフトウェアAがソフトウェアXにユーザーXのデータにアクセスする方法」の問題を調べます。

この実装は、ユーザーがトークンを取得し、サードパーティのサービスが「はい、このユーザーとこのトークンが一致し、データを今すぐ取得できます」という標準的な方法です。

ただし、基本的に、OAuth 2.0は単なるトークンプロトコルです。他のトークンプロトコルと同じプロパティを示します-それらのトークンを保護するにはSSLが必要です-トークンの生成方法を変更するだけです。

OAuth 2.0が役立つ2つの方法があります。

  • 他者に認証/情報を提供する
  • 他からの認証/情報の取得

しかし、最終的には、トークンを使用するだけです。

質問に戻る

したがって、あなたが尋ねている質問は、「トークンをCookieに保存し、環境の自動セッション管理で詳細を処理する必要がありますか、それともJavascriptにトークンを保存し、それらの詳細を自分で処理する必要がありますか?」

そして答えは:あなたを幸せにするものは何でもしてください

ただし、自動セッション管理に関することは、舞台裏で多くの魔法が発生するということです。多くの場合、これらの詳細を自分で管理する方が適切です。

私は21歳ですので、SSLはイエスです

もう1つの答えは、すべてにhttpsを使用するか、ブリガンドがユーザーのパスワードとトークンを盗むことです。

435
Curtis Lassam

JWT(JSON Web Tokens)とSSL/HTTPSを使用することで認証プロセスのセキュリティを強化できます。

基本認証/セッションIDは、次の方法で盗まれる可能性があります。

  • MITM攻撃(中間者) - SSL/HTTPSなし
  • ユーザのコンピュータにアクセスする侵入者
  • XSS

JWTを使用することで、ユーザーの認証情報を暗号化してクライアントに格納し、それをAPIへのすべての要求とと​​もに送信します。サーバー/ APIはトークンを検証します。 秘密鍵(サーバー/ APIが秘密に保管する)がないと、復号化/読み取りできません。 更新を読みます

新しい(より安全な)フローは次のようになります。

ログイン

  • ユーザーがログインしてログイン資格情報をAPIに送信する(SSL/HTTPS経由)
  • APIがログイン認証情報を受け取る
  • 有効な場合:
    • データベースに新しいセッションを登録する読み込み更新
    • 秘密鍵を使用してJWTのユーザーID、セッションID、IPアドレス、タイムスタンプなどを暗号化します。
  • APIはJWTトークンをクライアントに送り返します(SSL/HTTPS経由)
  • クライアントはJWTトークンを受け取り、localStorage/cookieに保管します

APIへのすべてのリクエスト

  • ユーザーがHTTPヘッダーに格納されたJWTトークンを使用してHTTP要求をAPI(SSL/HTTPS経由)に送信する
  • APIはHTTPヘッダーを読み取り、その秘密鍵でJWTトークンを復号化します
  • APIはJWTトークンを検証し、HTTP要求からのIPアドレスをJWTトークン内のものと突き合わせ、セッションが期限切れになったかどうかを確認します。
  • 有効な場合:
    • 要求された内容で応答を返す
  • 無効な場合:
    • 例外を投げる(403/401)
    • システムへの侵入フラグ
    • 警告メールをユーザーに送信します。

更新30.07.15:

JWTのペイロード/クレームは実際には秘密鍵(secret)がなくても読み取ることができ、localStorageに格納するのは安全ではありません。私はこれらの誤った声明についてすみません。しかし彼らは、 JWE標準(JSON Web Encryption) に取り組んでいるようです。

私はJWTにクレーム(userID、exp)を格納し、API /バックエンドが知っているだけの秘密鍵(secret)で署名し、それを安全なHttpOnlyクッキーとしてクライアントに格納することでこれを実装しました。この方法では、XSSを介して読み取ることはできず、操作することもできません。そうしないと、JWTは署名の検証に失敗します。また、セキュアなHttpOnlyCookieを使用することで、CookieがHTTPリクエスト経由でのみ送信され(スクリプトからアクセスできない)、セキュア接続経由(HTTPS)でのみ送信されるようになります。

17.07.16を更新しました。

JWTは本来、ステートレスです。それは彼らが自分自身を無効にするか、または失効させることを意味します。トークンの有効性は署名の検証と有効期限にのみ依存するのではなく、トークンの要求にSessionIDを追加してステートフルにすることで、サーバーのセッション状態にも依存します。ただし、利点はトークン/セッションを簡単に無効にできることです。これまでステートレスJWTでは不可能でした。

49
Gaui

私は第二に、トークンシステムに行きます。

ember-auth または ember-simple-auth について知っていましたか?どちらもember-simple-authのようにトークンベースのシステムを使用しています。

Ember.jsアプリケーションでトークンベースの認証を実装するための軽量で目立たないライブラリ。 http://ember-simple-auth.simplabs.com

彼らはセッション管理を持ち、既存のプロジェクトにも簡単に接続できます。

Ember-simple-authのEmber App Kitサンプルバージョンもあります。 OAuth2認証にember-simple-authを使用したember-app-kitの動作例

7
DelphiLynx