web-dev-qa-db-ja.com

JWTのRSA署名について

JWT(JSON Web Token)スキームの助けを借りて、サインインシステムを実装しています。基本的に、ユーザーがサインイン/ログインした後、サーバーはJWTに署名してクライアントに渡します。

その後、クライアントは各リクエストでトークンを返し、サーバーは応答を返す前にトークンを検証します。

これはあなたが期待する方法とほぼ同じですが、プロセスのロジックに問題があります。私が読んだすべての数学記事から、RSA署名は署名に非対称キーを使用しているようです。その名前が示すように、公開鍵はクライアントに公開され、秘密鍵はサーバー上に保持されるため、クライアントに送信される公開鍵でJWTに署名し、サーバー側でそれを使用して検証することは理にかなっています秘密鍵。

しかし、私が見るすべての例とライブラリでは、逆のように見えます。なぜそうなのか、考えはありますか? JWTが秘密鍵で署名され、公開鍵で検証された場合、その意味は何ですか?

21
Liran Cohen

まず、謝罪、この答えはかなり長くなりました。

RSAを使用してトークンに署名し、接続しているクライアントがWebブラウザーである場合、クライアントにはRSAキー(パブリックまたはプライベート)は表示されません。これは、クライアントはおそらくJWTが有効であることを確認する必要がなく、サーバーだけがそれを行う必要があるためです。クライアントは単にJWTを保持し、要求されたときにサーバーに表示します。次に、サーバーは、トークンを見つけたときにその有効性を確認します。

それでは、なぜJWTに公開鍵/秘密鍵のコンボが必要なのでしょうか?まず最初に、公開/秘密キーアルゴリズムを使用する必要はありません。

JWTには多くの異なるアルゴリズムを使用して署名できます。RSAはそのうちの1つです。 JWTに署名するための他の一般的な選択肢は、ECDSAまたはHMACアルゴリズムです(JWT標準は others をサポートします)。 HMACは、具体的には、公開/秘密キースキームではありません。トークンの署名と検証の両方に使用されるキーは1つだけです、キー。これは、JWTの署名と検証の両方に秘密鍵を使用していると考えることができます。私はこれについては決して専門家ではありませんが、最近自分の研究を行った結果、次のような結論に達しました。

HMACを使用するのは、最速のオプションであるため便利です。ただし、JWTを検証するには、すべてを実行する1つのキーを誰かに与える必要があります。このキーを他の誰かと共有すると、その人はsignトークンと彼らがあなたのようにふりをします。すべてがJWTを検証できるようにする必要がある複数のサーバーアプリケーションを構築している場合、すべてのアプリケーションがトークンに署名する機能を持たせたくない場合があります人々はセキュリティリスクなどです)。この場合、厳密に制御された1つの秘密キー(および署名を行う1つのアプリ)を用意し、公開キーを他のユーザーと共有して、トークンを検証できるようにすることをお勧めします。ここで、秘密鍵はトークンの署名に使用され、公開鍵はトークンの検証に使用されます。この場合、RSAまたはECDSAを選択します。

たとえば、すべてが同じデータベースに接続するアプリのエコシステムがあるとします。ユーザーをログインさせるために、各アプリは専用の「ログイン」アプリにユーザーを送ります。このアプリには秘密鍵があります。他のアプリは、公開鍵を使用してユーザーがログインしていることを確認できます(ただし、ユーザーはログインできません)。

私が行った研究は、このシナリオではほとんどのJWTアプリにとってRSAがより良い選択肢であることを示しています。これは、理論的にはアプリがトークンを頻繁に検証するためです。 RSAは、検証時のECDSAよりもはるかに高速です。 ECDSAは、キーのサイズが小さいため、主にニースです。これにより、公開鍵をクライアントのブラウザーに送信する必要があるため、HTTPS証明書の方が適切になります。ただし、JWTシナリオでは、キーはサーバー上にとどまっているため、ストレージサイズはn/aであり、検証速度がより重要です。

結論:複数の小さな「マイクロサービスアプリ」なしで小さなアプリを構築している場合/あなたが唯一の開発者である場合は、おそらくHMACを選択してキーを暗号化します。それ以外の場合は、おそらくRSAを選択してください。繰り返しになりますが、私は専門家ではなく、このトピックを最近グーグルで検索したばかりの人です。

39
John

あなたの提案:

クライアントに送信される公開鍵でJWTに署名し、秘密鍵を使用してサーバー側でそれを検証することは理にかなっています。

正しくありません。署名はサーバーの秘密鍵で行われ、暗号化はクライアントの公開鍵で行われます。これが、PKIの一般的な仕組みです。

11
Hans Z.

データの署名/検証と暗号化/復号化には違いがありますが、セマンティクスは似ています。

制御されたソースのみが持つ秘密鍵でデータに署名するため、情報を受信する人は誰でもあなたの公開鍵を使用して、この情報が実際に送信されたものであり、あなたが送信しようとした情報と同じであることを検証できます.

公開鍵でデータを暗号化し、秘密鍵で復号化します。これは逆に聞こえますが、実際には署名と同じ論理的概念に従います。人Aと人Bの間でデータを送信する場合は、両方の人が公開/秘密キーペアを持ち、彼らが会うとお互いに公開キーを共有します(ハンドシェイク)。 AはBのメッセージを作成し、Bの公開キーを使用してそれを暗号化し、Bに送信します。Bの秘密キーを持たない人は、Aを含むメッセージを最初に送信しても、解読できません。

JWTの観点では、JWTペイロード自体は、いくつかの標準化されたフィールドを持つBase64でエンコードされたJSONです。この署名により、公開鍵を持っている人が、中間の人によって情報が変更されていないことを検証できます。チェックサムに似ていますが、いくつかの特別なセキュリティベースの温かいあいまいな感じがあります。署名されたJWTのコンテンツは、エンドユーザーと中間の誰でも簡単に見ることができます(暗号化ではなく、base64はunicodeまたはutf-8のようなエンコードです)。 [〜#〜] pii [〜#〜]

他の人が述べたように、ほとんどのJWTには、クライアント向けではなく、RESTfulサービスのステートレス部分を促進するための情報が含まれています。一般に、JWTにはaccountid、userid、および多くの場合「クレーム」としての許可が含まれます。 APIエンドポイントは、署名を検証し、クライアントによって変更されないというクレームを合理的に信頼できます。クライアントがリクエストごとにJWTを送信すると、エンドポイントは単に公開キーで署名を検証するだけで、現在地を取得するために多くのデータベースをやり取りする必要がなくなります。

さらに、署名されたJWTは暗号化できます。 JWE仕様 によると、ペイロードは署名後に暗号化され、検証前に復号化されます。ここでのトレードオフは、すべてのエンドポイントにJWTを復号化するための秘密鍵も必要ですが、エンドユーザーはJWTのコンテンツを表示できないことです。一般に、秘密鍵は安全に保たれることを意図しており、広く配布された秘密鍵は安全性が低いため、トレードオフと言います。暗号化のセキュリティ、リスク評価、費用/便益は他のすべての獣です:)

2
evillive