web-dev-qa-db-ja.com

CSRF保護とリフレッシュトークンを備えたJWTを安全に使用する

私は自分のアプリにJWTを実装していますが、できる限り安全にしたいと考えています。私は計画しているすべてのものをレイアウトします。この実装のセキュリティに関する提案があれば大いに感謝します。これは私のサイトです。フロントエンドとバックエンドの両方のあらゆる側面に完全にアクセスできます。

これは、APIを使用してバックエンドにアクセスするSPAです。 JWTを使用して、APIヒットごとにDB呼び出しを保存しています。

JWT

JWTはaccess_token Cookieに保存されます。最初に署名され、次に jose-jwt を使用して暗号化されます。署名アルゴリズムはHS256、暗号化はDIRです。これには、ユーザーのID、有効期限expクレーム、およびその他のいくつかのカスタムクレームが含まれます。 JWTは30分で期限切れになり、JWT Cookieは7日で期限切れになります。

(短縮版):

  • Cookieに保存されたJWT
  • JWTにはユーザーIDが含まれます
  • JWTに署名してから暗号化
  • JWT expクレームは30分後に設定されます
  • JWT Cookieが7日後に期限切れになるように設定

CSRF保護

JWTには、ランダムに生成されたCSRFトークンを格納するcstクレームが含まれています。 CSRFトークンは、ログイン時と新しいJWTの発行時に応答本文で送信されます。 CSRFトークンはブラウザのlocalStorageに保存されます。リクエストごとに送信され、JWTの値に対して検証されます。

(短縮版):

  • JWTにはランダムに生成されたCSRFトークンが含まれています
  • ログイン時に送信され、localStorageに保存されるCSRFトークン
  • すべてのリクエストのリクエストヘッダーで送信されるCSRFトークン
  • JWTのCSRFトークンと比較したヘッダーCSRFトークン

トークンの更新

JWTは30分で有効期限が切れるので、更新する必要があります。 JWTには、ランダムなリフレッシュトークンを格納するrfsクレームが含まれています。この更新トークンは、DB(複数のセッションを可能にするユーザーとは別のテーブル)にも格納されます。 JWTの有効期限が切れた場合(expクレームに基づいて)、DBがチェックされ、ユーザーがまだ有効であることを確認します(アカウントが削除されていない、パスワードが変更されていないなど)。ユーザーが有効な場合、更新トークンが検証され、新しいJWT/CSRFトークンが生成されて応答で返されます。ユーザーが無効な場合、access_token0のような任意の値で送り返され、有効期限は過去に設定されるため、ブラウザーはそれをクリアします。 CSRFトークンは空に戻されるため、localStorageからクリアされます。ユーザーのすべての更新トークンがDBから消去されます。

(短縮版):

  • JWTの有効期限が切れた場合は、ユーザーDBをチェックして、ユーザーがまだ有効であることを確認します
  • 有効な場合:
    • リフレッシュトークンとDBを比較します(これ以降は一致すると仮定します)
    • 新しい更新トークンを生成し、DB内の以前の値を上書きします
    • 新しいexp日付でJWTを再発行します
    • 更新トークンを渡し、localStorageに保存します
  • 無効な場合:
    • A)無効な値に設定し、b)有効期限を過去に設定して、JWT Cookieをクリアします。
    • LocalStorage CSRFトークンをクリアするようにブラウザに指示する
    • DBからユーザーのすべての更新トークンをクリアする

すばやく汚れたTL; DR

  • ログイン時に、ランダムなCSRFトークンをJWTに追加します。
  • 同じCSRFトークンを応答本文でクライアントに送信します。
  • CSRFトークンをlocalStorageに保存します。
  • JWTに更新トークンを含めます。
  • 1週間後に有効期限が切れるようにJWT Cookieを設定します。
  • JWT expクレームを30分に設定します。
  • JWTクレームの有効期限が切れている場合は、DBに対して更新トークンを確認して、ユーザーがまだ有効であることを確認します。
  • ユーザーが有効な場合:
    • 新しいCSRFトークンと新しい更新トークンで更新されたJWTを発行します。
    • JWT Cookieの有効期限を今後1週間に設定します。 (基本的にクッキーを再発行します)
    • 応答の本文で新しいCSRFトークンを送信し、既存のlocalStorage値を上書きします。
  • ユーザーが無効な場合:
    • 同じ名前でコンテンツのないJWT Cookieを返します。
    • Cookieの有効期限を過去の任意の日付に設定します。
    • LocalStorage値をクリアするようにブラウザに指示します。
11
vaindil

あなたはいくつかの異なるテクノロジーを混同しているようであり、なぜこれらのテクノロジーを選択したのか、なぜそれらが保護しようとしている脅威を制御しているのか明確にしていない。

JWTはaccess_token Cookieに格納されます。最初に署名され、次にjose-jwtを使用して暗号化されます。

暗号化される理由はありますか?署名は、データの整合性を確認する場合に使用されます。つまり、データが鍵なしでだれによっても変更されていない場合です。暗号化は、データの機密性を保護する場合に使用されます。つまり、キーがないと誰も読み取ることができません。トークンにエンドユーザーが読み取ることができないはずのものがない場合、暗号化する理由はありません。

JWTには、ランダムに生成されたCSRFトークンを格納するcstクレームが含まれています。 CSRFトークンは、ログイン時と新しいJWTの発行時に応答本文で送信されます。 CSRFトークンはブラウザのlocalStorageに保存されます。リクエストごとに送信され、JWTの値に対して検証されます。

これは Double Submit Cookie CSRFコントロールの実装のように聞こえます。 localStorageは Same Origin Policy によって保護されており、ユーザーのブラウザーの別のWebセッションがトークンにアクセスできないようにします。

CSRFトークンに少なくとも128ビットのエントロピーがあることを確認してください。

JWTは30分で有効期限が切れるので、更新する必要があります。 JWTには、ランダムなリフレッシュトークンを格納するrfsクレームが含まれています。この更新トークンは、DB(複数のセッションを可能にするユーザーとは別のテーブル)にも格納されます。 JWTが(expクレームに基づいて)期限切れになると、DBがチェックされ、ユーザーがまだ有効であることを確認します(アカウントが削除されていない、パスワードが変更されていないなど)。ユーザーが有効な場合、更新トークンが検証され、新しいJWT/CSRFトークンが生成されて応答で返されます。

これは物事が混乱するところです。セキュリティモデルは、クライアント側セッションまたはサーバー側セッションのどちらかを決定する必要があります。前者には通常JWTが使用されます。つまり、サーバーでのみ利用可能なキーで署名されたトークンがクライアントにある場合、アプリケーションは、このトークンの存在が有効なセッションを示し、有効期限が切れておらず、署名が有効であることを信頼している必要があります。

この方法の欠点は、トークンを取り消すことが難しいことです。ユーザーアカウントが侵害され、ユーザーがパスワードを変更した場合、有効で期限切れではない署名済みトークンがまだあるため、攻撃者のセッションは30分間アクティブのままになります。

これに対する修正は、代わりにサーバー側セッションを実装することです。たとえば、データベーステーブルでセッションを追跡します。つまり、データベースの行を削除することで、セッションから即座にログアウトできます。セッショントークンは128ビットのエントロピーランダム文字列で、Cookieのクライアント側として提供され、データベースからのデータ漏洩を軽減するためにSHA-256サーバー側でハッシュされて保存されます。いつでもプレーンテキストの有効期限をCookieと一緒に送信できるため、クライアントはいつトークンを更新する必要があるかを知ることができます。例えばトークンのHttpOnly Cookie、および有効期限が含まれる非HttpOnly Cookie。これにより、クライアント側のJavaScriptがそれを読み取ることができます。 HttpOnly Cookieは、XSS欠陥の影響を軽減するのに役立ちます。

したがって、サーバー側でセッションを追跡している場合、署名されたJWTをクライアント側に持つ利点はほとんどありません。コードが増えると、攻撃対象が増え、余剰コードに脆弱性が導入される可能性が高くなります。多くの場合、アプリケーションの複雑さはセキュリティの逆です。

セッション状態のサーバー側セッションと組み合わせてDouble Submit Cookieを使用している場合は、CSRFトークンに別のCookieを使用することをお勧めします。これにより、セッション識別子トークンを危険にさらすことなく、クライアント側コードを使用してヘッダーとしてCSRFトークンを追加できます。 CORSを実装せずにカスタムヘッダーを設定する場合は、 これによりCSRFが多少緩和される であることに注意してください。ただし、Flashのようなテクノロジーはブラウザーのセキュリティを混乱させる傾向があるため、トークンも使用することをお勧めします(Flashはより多くのコードを実行し、より多くのコードはより多くの攻撃面を提供し、より多くのコードは脆弱性の可能性を意味します)。

11
SilverlightFox

これは、リフレッシュトークンの部分まではかなり健全に聞こえます(CSRFソリューションもかわいいです)。

JWTベースのシステムで私が目にする利点の1つは、アクセストークンが定期的に期限切れになることです。つまり、侵害されたJWTアクセストークンは、攻撃者に数分または数時間のみアクセスを許可します。

JWTに更新トークンを含めると、攻撃者はJWTを危険にさらして簡単にそれを更新して、有効期限を無効にすることができます。

アクセストークンは、新しいアクセストークンを付与できません。新しいアクセストークンを付与できるのは、更新トークン(または完全認証)のみです。

4
ProdigySim

コメントからの集計:

  • Ajaxを介してサーバー側APIと通信するシングルページアプリのベアラー資格情報としてのJWTは理にかなっています。
  • シングルページアプリでのCookieの使用は意味がありません。サーバーAPIへのすべてのリクエストがajax関数を介してボトルネックになっている場合、その関数はヘッダーにJWTを含めることができます。 Cookieがない場合、CSRFはありません。攻撃フレームからの要求はajax関数を通過しないためです。
  • もちろん、サードパーティのスクリプトの使用は蔓延していますが、それでも大きなセキュリティリスクがあります。サードパーティのスクリプトは同じjavascriptとDOMコンテキスト内で動作し、DOM全体を見ることができます。それらが悪意のあるものであれば、多くの攻撃オプションがあります。可能であれば、サードパーティのスクリプトを削除するか、サブリソースの整合性またはその他の手段(変更を監視するなど)を使用して、スクリプトが検証されていることを確認します。
  • localStorageは同じOriginポリシーに従い、DOMまたは認証データのクライアントキャッシュのように扱うことができます。 SPAでは、JWTはDOMと同じセキュリティモデルに従うため、localStorageに保持できます。
  • 更新トークンはJWTに保持できます
  • JWTの有効期限と更新の有効期間については、抽象的にコメントするのは困難です。セキュリティと使いやすさの適切なバランスを見つけること、十分な理由がない限りユーザーがフープを飛び越えることを要求しないこと、そして懸念がある場合に適切な保証をユーザーに提供することが重要です。

他にすべきこと:

  • Httpsを適用し、x-frame-options、厳格なトランスポートセキュリティ、コンテンツセキュリティポリシーなどの関連するリソースプロトコルヘッダーを使用する
  • 特定のバージョンのシングルページアプリのメタデータをJWTに保持し、アプリの更新後に更新を要求する
  • 特定のクライアントのメタデータをJWTに保持します。モバイルブラウザーからデスクトップブラウザーJWTの使用を許可しません。
  • サーバーから、ユーザーに関連付けられたすべての未解決のJWTと更新トークンを取り消すことができます。ユーザーが電話とデスクトップに関連付けられたJWTと更新トークンを持っていて、電話を紛失したと通知された場合、おそらくユーザーの電話とデスクトップJWTと更新トークンを無効にする必要があります。
  • サーバーAPIで強力なオブジェクト/操作レベルの承認を持っている-つまり、JWTの整合性を検証し、パッケージ化された承認が取り消されていないことを確認し、それに含まれるクレームがAPIへの適切なアクセスを提供していることを確認してから、 APIオペレーション。
2
Jonah Benton