web-dev-qa-db-ja.com

フロントエンドとバックエンドのJWTトークン戦略

私は、emberjsでフロントエンドを使用し、nodejsサーバーでバックエンド/サーバー側を使用してアプリケーションを作成しています。ユーザーがサードパーティOauth(google、Twitter、Facebook)でログイン/サインアップできるようにemberjsを構成しました。RESTfulAPIをホストするエクスプレスnodejsサーバーで記述されたバックエンドがあります。

私はDBをemberjsに接続しておらず、厳密にはクライアント側のコードなので、とにかくすべきではないと思います。クライアント側とサーバー側の間の通信にJWTを使用することを計画しています。ユーザーがoauth credでログインすると、プロバイダーからuid、name、login、access_tokenおよびその他の詳細を含むJSONオブジェクトが返されます。

ユーザーサインアップの処理方法に関する戦略の選択に苦労しています。 OAuthであるため、サインアッププロセスはありません。したがって、フローは、ユーザーがデータベースにない場合は作成します。メール/パスワード認証をサポートしていません。ユーザーがOAuthプロバイダーで初めてサインインするときのフローはどうなりますか?emberjsは、サインインごとにすべての詳細をバックエンドに送信して、バックエンドが新しいユーザーを追加できるようにしますdb?

JWT本体の一部は何ですか?私はuidとプロバイダーがアクセストークンを提供することを考えていました。ここで考えられる問題の1つは、プロバイダー固有のアクセストークンが変更される可能性があることです。ユーザーは、プロバイダーのサイトからトークンを取り消して、emberjsで再度サインアップできます。

他のjavascriptクライアントサイドフレームワークでフロントエンドを作成するのは、それが簡単になれば、私はオープンです。

30
ed1t

動作だけでなく、セキュアなステートレス認証についても話している場合は、accessrefreshの両方のトークンを使用した適切な戦略を検討する必要があります。

  1. アクセストークンは、保護されたリソースへのアクセスを提供するトークンです。 Expirationここでは、約1時間でインストールされる場合があります(考慮事項によって異なります)。

  2. リフレッシュトークンは、期限切れになった場合やユーザーセッションが更新された場合に追加のaccess tokenを生成するために使用される特別なトークンです。明らかに、(access tokenと比較して)長寿命にし、可能な限り安全にする必要があります。 Expirationここでは、約10日またはそれ以上でインストールされる場合があります(考慮事項にも依存します)。

FYI:refresh tokensは長寿命であるため、本当に安全にするために、データベースに保存することをお勧めします(トークンリクエストの更新が実行されます)めったにありません)。このように、リフレッシュトークンが何らかの方法でハッキングされ、誰かがaccess/refreshトークンを再生成した場合でも、もちろん権限を失いますが、ログイン/パスを知っているので、システムにログインできます(後で使用する場合)またはソーシャルネットワーク経由でサインインするだけです。


これらのトークンを保存する場所は?

基本的に2つの一般的な場所があります。

  1. HTML5 Web Storage(localStorage/sessionStorage)

いいのですが、同時に十分に危険です。ストレージは、同じドメインのJavaScriptコードを介してアクセスできます。つまり、 [〜#〜] xss [〜#〜] を取得した場合、トークンがハッキングされる可能性があります。したがって、この方法を選択する場合は、信頼できないすべてのデータを注意してエンコード/エスケープする必要があります。そして、たとえそれを行ったとしても、サードパーティのクライアント側モジュールを使用することはかなり確実であり、それらのいずれかに悪意のあるコードがあるという保証はありません。

また、Web Storageは、転送中に安全な標準を強制しません。したがって、JWTがHTTPSを介して送信され、HTTPを送信しないようにする必要があります。

  1. Cookies

特定のHttpOnlyオプションを使用すると、CookieはJavaScriptを介してアクセスできず、XSSの影響を受けません。 Secure Cookieフラグを設定して、CookieがHTTPS経由でのみ送信されるようにすることもできます。ただし、Cookieは異なるタイプの攻撃に対して脆弱です。クロスサイトリクエストフォージェリ( [〜#〜] csrf [〜#〜] )。この場合、CSRFは、ある種の同期トークンパターンを使用することで防止できます。 AngularJSSecurity Considerations セクションに適切な実装があります。

記事 に従ってください。

一般的な仕組みを説明するには:

http://jlabusch.github.io/oauth2-server/img/diag_refresh_token.png


[〜#〜] jwt [〜#〜]自体に関するいくつかの言葉:

明確にするために、Auth0のユーザーからの本当にクールな JWTデバッガー があります。 publicprivate(および reserved )という2つの(場合によっては3つの)一般的なクレームタイプがあります。

JWT bodyの例(ペイロード、好きなものを指定できます):

{     
  name: "Dave Doe",
  isAdmin: true,
  providerToken: '...' // should be verified then separately
}

JWT構造の詳細については、 here を参照してください。

32
oleh.meleshko

あなたが提示した2つの特定の質問に答えるには:

ユーザーがOAuthプロバイダーで初めてサインインするときのフローはどうなりますか?emberjsは、サインインごとにすべての詳細をバックエンドに送信して、バックエンドが新しいユーザーを追加できるようにしますdb?

ユーザーがサインアップするか、oauth経由でログインし、クライアントが新しいアクセストークンを受信するたびに、ユーザーテーブルにpsert(更新または挿入)します) (またはコレクション)とともに、oauthプロバイダーAPIからユーザーについて取得した新しい情報または更新された情報。アクセストークンと関連するプロファイル情報を確保するために、各ユーザーレコードに直接保存することをお勧めします通常、私は通常、これを新しいトークンが存在するときにこれらの手順を自動的に実行する何らかのミドルウェアに構成します。

JWT本体の一部は何ですか? uidとプロバイダーがアクセストークンを提供することを考えていました。ここで考えられる問題の1つは、プロバイダー固有のアクセストークンが変更される可能性があることです。ユーザーは、プロバイダーのサイトからトークンを取り消して、emberjsで再度サインアップできます。

JWT本体は通常、sers Claimsで構成されます。私は個人的には、プロバイダーのアクセストークンをJWTトークンの本体に格納しても、クライアントアプリにとってはほとんどメリットがないため、ほとんど利点がないと考えています(クライアントからAPIに直接API呼び出しをたくさん行う場合を除き、これらはサーバー側を呼び出し、アプリクライアントに、独自のインターフェイスに準拠する正規化されたクレームセットを送り返します)。独自のクレームインターフェイスを記述することにより、クライアントアプリの複数のプロバイダーから生じるさまざまな違いを回避する必要がなくなります。この例としては、APIでユーザープロファイルテーブルに保存する共通フィールドとは異なる名前のTwitterおよびFacebook固有のフィールドを結合し、ローカルプロファイルフィールドをJWT本文のクレームとして埋め込み、クライアントアプリによって解釈されるようにします。これには、暗号化されていないJWTトークンで将来漏洩する可能性のあるデータを永続化しないという追加の利点があります。

oauthプロバイダーが提供するアクセストークンをJWTトークン本体内に保存するかどうかに関係なく、プロファイルデータが変更されるたびに新しいJWTトークンを付与する必要があります。プロファイルの更新が発生せず、以前のトークンがまだ有効な場合、新しいJWTトークンの発行をバイパスします)。

JWTトークン本文にクレームとして保存するプロファイルフィールドに加えて、標準の JWTトークン本文フィールド を常に定義します。

{
    iss: "https://YOUR_NAMESPACE",
    sub: "{connection}|{user_id}",
    aud: "YOUR_CLIENT_ID",
    exp: 1372674336,
    iat: 1372638336
}
5
cchamberlain

すべてのOAuth=ワークフローでは、間違いなく passportjs ライブラリを使用する必要があります。完全なドキュメントも読む必要があります。理解しやすいですが、 OAuth 300以上のプロバイダーと発行トークンによる認証が含まれています。

それでも、手動で行う場合や基本的な理解が必要な場合は、ここで使用するフローを示します。

  1. フロントエンドには、OAuthが実装されているGoogle/Facebookなどでのサインインをリストするログインページがあります。

  2. 成功OAuthはuid、login、access_tokenなどになります(JSONオブジェクト)

  3. POST Node.jsアプリケーションの_/login/_ルートへのJSONオブジェクト。(はい、新規ユーザーか既存ユーザーかに関係なく、応答全体を送信します。ここで追加データを送信します。 2つのリクエストを行うよりも優れています)

  4. バックエンドアプリケーションは、uidおよび_access_token_を読み取ります。 _access_token_が有効であることを確認するには、( https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken )または質問してくださいアクセストークンを使用するプロバイダーからのユーザーデータ用。 (OAuthアクセストークンはアプリ/開発者ごとに生成されるため、無効なアクセストークンでは失敗します))次に、バックエンドDBを検索します。

  5. uidがデータベースに存在する場合、DBでユーザーのaccess_tokenとexpiresInを更新します。 (access_tokenを使用すると、特定のユーザーについてFacebookからより多くの情報を取得でき、通常は数時間アクセスできます。)

  6. それ以外の場合は、uid、loginなどの情報を持つ新しいユーザーを作成します。

  7. Access_tokenを更新するか、新しいユーザーを作成したら、uidを含むJWTトークンを送信します。 (jwtをシークレットでエンコードします。これにより、送信され、改ざんされていないことが保証されます。チェックアウト https://github.com/auth0/express-jwt

  8. ユーザーが_/login_からjwtを受け取った後のフロントエンドで、sessionStorage.setItem('jwt', token);によってsessionStorageに保存します

  9. フロントエンドで、次も追加します。

if ($window.sessionStorage.token) { xhr.setRequestHeader("Authorization", $window.sessionStorage.token); }

これにより、jwtトークンがある場合、すべてのリクエストで送信されます。

  1. Node.js app.jsファイルで、追加します

app.use(jwt({ secret: 'shhhhhhared-secret'}).unless({path: ['/login']}));

これにより、パス内のすべてのjwtが検証され、ユーザーがログインしていることが確認されます。そうでない場合は、ログインページへのアクセスとリダイレクトが許可されません。ここでの例外ケースは_/login_です。これは、新規ユーザーまたは非認証ユーザーの両方にJWTを提供する場所だからです。

Github URLでトークンを取得する方法と、現在サービスを提供しているユーザーのリクエストを確認する方法に関する詳細情報を見つけることができます。

3
Rahat Mahbub