web-dev-qa-db-ja.com

外部公開のセキュリティトークン:UUIDまたはHMAC / JWT /ハッシュ?

私はWebアプリのバックエンドを構築しています。新しいユーザーがサイトにアクセスしてSign Upボタンをクリックすると、ユーザーはユーザー名とパスワードを要求する非常にシンプルなフォームに入力し、彼らは提出します。これにより、サーバーに確認メールをそのメールアドレスに送信するように求められます。次に、メールを確認し、リンク(メールを確認する)をクリックすると、ログインページに転送され、必要に応じてサインインできます。

メールを検証するには、サーバーがメールを生成するときに、検証トークン(おそらくUUID)を作成(および保存)して添付する必要があります。メールのこのリンクにリンクすると、リンクは次のようになります。

" https://api.myapp.example.com/v1/users/verify?vt=12345 "

ここで、vt=12345は「確認トークン」です(おそらくUUIDです)。したがって、ユーザーがこのリンクをクリックすると、私のGET v1/users/verifyエンドポイントがトークンを確認し、トークンが有効であることを確認して、ユーザーを「アクティブ化」するためにいくつかのDB更新を行います。ログインできます。

ユーザーが電子メールの受信を解除する場合、またはパスワードを思い出せず、ログインするためにパスワードを回復する必要がある場合の同様のシナリオ。

退会

ユーザーはメールの受信を停止したいが、それでもアプリを使用したい。送信する週刊ニュースレターの「Unsubscribe」リンクをクリックします。このリンクには、上記の確認トークンと同様に、サーバー上に生成され+保存され、ユーザーの電子メールからの退会リクエストを認証するために使用される、同様の「退会トークン」が含まれている必要があります。

パスワードを回復

ここで、ユーザーは自分のパスワードを忘れており、それを回復する必要があります。そのため、ログイン画面で[Forgot my password]リンクをクリックすると、メールアドレスを入力するフォームが表示されます。サーバーはそのアドレスにメールを送信します。彼らはこのメールをチェックし、新しいメールアドレスを入力できるフォームへのリンクが含まれています。このリンクには、「リセットトークントークン」を含める必要があります。これは、上記の確認トークンと同様に、サーバー上で生成および保存され、ユーザーのパスワード変更リクエストを認証するために使用されます。

したがって、ここには解決すべき3つの非常に類似した問題があり、すべて "one-time only(OTO)security tokens"と呼ばれるものを使用する必要があります。これらのOTOトークン:

  • サーバー側で生成して永続化する必要があります(おそらくsecurity_tokensテーブルに)
  • メールの内部から公開するリンクに添付できるものである必要があります
  • 1回のみ有効である必要があります。一度クリックすると、トークンは「使用」され、再利用できません

私の質問

私が思いついた解決策はシンプルで、ほとんどシンプルすぎました。

トークンについては、ランダムなUUID(36文字)を生成して、次のフィールドを持つsecurity_tokensテーブルに格納しています。

[security_tokens]
---
id (PK)
user_id (FK to [users] table)
token (the token itself)
status (UNCLAIMED or CLAIMED)
generated_on (DATETIME when created)

サーバーがそれらを作成すると、それらは「クレームされません」。ユーザーがテーブル内のリンクをクリックすると、「クレーム済み」になります。バックグラウンドワーカージョブは定期的に実行され、CLAIMEDトークンをクリーンアップするか、「generated_onフィールドに基づいて」「期限切れ」のUNCLAIMEDトークンを削除します。アプリは、以前にクレームされた(まだクリーンアップされていない)トークンも無視します。

私はこのソリューションはうまくいくと思いますが、私はスーパーセキュリティの人ではなく、このアプローチが心配です:

  1. おそらく私のアプリをある種の攻撃/エクスプロイトにさらしたままにします。そして
  2. 他のソリューションも同様に機能する可能性がある場合、おそらく車輪を再発明します

上記の2番目のように、死んでいる単純なUUIDではなく、ハッシュ/ HMAC/JWT関連のメカニズムを使用する必要があるのか​​どうか疑問に思っています。たぶん、これらのトークンにCLAIMステータスと有効期限を安全/不変の方法で含めるようにする方法を見つけた賢い暗号/セキュリティ担当者がいるかもしれません。

1
smeeb

あなたが説明したことにいくつかの懸念があります:

  1. UUIDをどのように生成するかによって、それらはそれほど予測不可能ではないかもしれません-それらは 暗号学的に強力なトークンではない です。
  2. security_tokensテーブルに「目的」を記述しないでください。これにより、(コードによっては)意図されたものとは異なる目的で誰かがトークンを使用できるようになります。
  3. システムには大量のデータベーストラフィックが必要です。受信することが予想されるトラフィックの量に応じて、これが問題になる場合と問題にならない場合があります。

別の解決策は、(たとえば)JWTを使用し、署名付きデータに含めることです。

  1. 有効なユーザーID。
  2. それが有効である目的。

JWT自体が、有効期限を管理するための機能を提供します。これは「二重に費やす」ことができるトークンを生成しますが、それらが電子メールの検証または購読解除のためである場合、それは問題ではないことに注意してください。 (同じメールを2回検証することは問題ですか?)

これらのトークンの場合、必要なのはアプリケーションサーバーに格納されたシークレットだけであり、トークンを生成または使用するための追加のデータベース書き込みは発生しません。 (これもまた、スケーリングの問題であるため、アプリケーションに適用される場合と適用されない場合があります。)

1
David