JWTを取得してペイロードをデコードできる場合、どのように安全ですか?ヘッダーからトークンを取得し、ペイロードのユーザー情報をデコードして変更し、同じ正しいエンコードされたシークレットで送り返すことはできませんか?
安全である必要があることは承知していますが、実際にテクノロジーについて理解したいと思います。何が欠けていますか?
JWTを取得してペイロードをデコードできる場合、どのように安全ですか?ヘッダーからトークンを取得し、ペイロードのユーザー情報をデコードして変更し、同じ正しいエンコードされたシークレットで送り返すことはできませんか?
安全である必要があることは承知していますが、実際にテクノロジーについて理解したいと思います。何が欠けていますか?
回答:
JWTは、署名、暗号化、またはその両方を行うことができます。トークンは署名されているが暗号化されていない場合、誰でもその内容を読み取ることができますが、秘密鍵がわからない場合は変更できません。そうでなければ、受信者は署名がもう一致しないことに気づくでしょう。
あなたのコメントへの回答:私があなたのコメントを正しく理解しているかどうかはわかりません。念のため、デジタル署名を知って理解していますか?ここでは、1つのバリアントについて簡単に説明します(HMACは対称ですが、他にもたくさんあります)。
アリスがJWTをボブに送信したいとします。彼らは両方ともいくつかの共有秘密を知っています。マロリーはその秘密を知りませんが、JWTを妨害して変更したいと考えています。それを防ぐために、アリスHash(payload + secret)
はこれを計算して署名として追加します。
メッセージを受信すると、ボブは計算Hash(payload + secret)
して署名が一致するかどうかを確認することもできます。ただし、Malloryがコンテンツの一部を変更すると、一致する署名を計算できなくなります(これはになりますHash(newContent + secret)
)。彼女はその秘密を知らず、それを見つける方法がありません。つまり、彼女が何かを変更すると、署名は一致しなくなり、ボブは単にJWTを受け入れなくなります。
仮に、私は別の人にメッセージを送信し{"id":1}
、で署名したとしHash(content + secret)
ます。(+はここでは単なる連結です)。私はSHA256ハッシュ関数を使用しており、取得した署名は次のとおり330e7b0775561c6e95797d4dd306a150046e239986f0a1373230fda0235bda8c
です。次はあなたの番です{"id":2}
。マロリーの役割を果たし、メッセージに署名してみてください。私が使った秘密がわからないのでできません。受信者が秘密を知っているとしたら、受信者はメッセージの署名を計算し、それが正しいかどうかを確認できます。
に移動しjwt.io
てトークンを貼り付け、内容を読み取ることができます。これは、最初は多くの人にとって不快です。
簡単に言えば、JWTは暗号化に関与しません。検証を気にします。つまり、「このトークンの内容は操作されましたか」という答えを常に得ることができますか?これは、サーバーがトークンを認識して無視するため、ユーザーによるJWTトークンの操作は無駄であることを意味します。サーバーは、クライアントにトークンを発行するときに、ペイロードに基づいて署名を追加します。その後、ペイロードと一致する署名を検証します。
論理的な問題は、暗号化されたコンテンツに自分自身を関与させない動機は何ですか?
最も単純な理由は、これがほとんどの部分で解決された問題であると想定しているためです。たとえば、Webブラウザーなどのクライアントを扱う場合、JWTトークンをsecure
(HTTP経由ではなく、HTTPS経由でのみ送信される)Cookieに保存し、httpOnly
(JavaScriptで読み取ることはできません)、サーバーと通信します。暗号化されたチャネル(HTTPS)。サーバーとクライアントの間に安全なチャネルがあることがわかったら、JWTまたはその他の必要なものを安全に交換できます。
これは事を簡単に保ちます。シンプルな実装により、採用が容易になりますが、各レイヤーで最も効果的なことを実行できます(HTTPSで暗号化を処理できます)。
JWTは、機密データを格納するためのものではありません。サーバーがJWTトークンを受信してそれを検証すると、独自のデータベースでユーザーIDを自由に検索して、そのユーザーの追加情報(アクセス許可、住所など)を取得できます。これにより、JWTのサイズが小さくなり、機密データがJWTに保持されないことを誰もが知っているため、不注意による情報漏洩を回避できます。
Cookie自体の動作とそれほど変わりません。多くの場合、Cookieには暗号化されていないペイロードが含まれています。HTTPSを使用している場合は、すべてが適切です。そうでない場合は、機密性の高いCookie自体を暗号化することをお勧めします。そうしないと、中間者攻撃が可能になることを意味します。プロキシサーバーまたはISPがCookieを読み取り、後であなたになりすましてそれらを再生します。同様の理由で、JWTは常にHTTPSのような安全なレイヤーで交換する必要があります。
json Webトークン(JWT)のコンテンツは本質的に安全ではありませんが、トークンの信頼性を検証するための組み込み機能があります。JWTは、ピリオドで区切られた3つのハッシュです。3番目は署名です。公開/秘密鍵システムでは、発行者は、対応する公開鍵でのみ検証できる秘密鍵でトークン署名に署名します。
発行者と検証者の違いを理解することが重要です。トークンの受信者は、それを検証する責任があります。
JWTをWebアプリケーションで安全に使用するには、2つの重要なステップがあります。1)暗号化されたチャネルを介して送信し、2)受信したらすぐに署名を検証します。公開鍵暗号の非対称性により、JWT署名の検証が可能になります。公開鍵は、JWTが対応する秘密鍵によって署名されたことを確認します。他のキーの組み合わせではこの検証を実行できないため、なりすましの試みを防止できます。これらの2つのステップに従ってください。JWTの信頼性を数学的に確実に保証できます。
続きを読む:公開鍵はどのようにして署名を検証するのですか?
初めから議論しましょう:
JWTは、Json Web Tokensを拡張する、非常にモダンでシンプルかつ安全なアプローチです。Json Webトークンは、認証のためのステートレスソリューションです。そのため、サーバーにセッション状態を保存する必要はありません。これは、もちろん、安静なAPIに最適です。Restful APIは常にステートレスである必要があり、JWTによる認証の最も広く使用されている代替手段は、セッションを使用してサーバーにユーザーのログイン状態を保存することです。ただし、当然のことながら、Restful APIはステートレスである必要があるという原則に従っていないため、JWTのようなソリューションが普及し、効果的になりました。
では、認証が実際にJson Webトークンでどのように機能するかを見てみましょう。データベースにユーザーがすでに登録されていると仮定します。したがって、ユーザーのクライアントは、ユーザー名とパスワードを使用してPOSTリクエストを行うことから始め、アプリケーションはユーザーが存在するかどうかを確認し、パスワードが正しい場合は、そのユーザーのみに固有のJson Webトークンを生成します。
トークンは、サーバーに格納されている秘密の文字列を使用して作成されます。次に、サーバーはそのJWTをクライアントに送り返し、クライアントはそれをCookieまたはローカルストレージに格納します。
このように、ユーザーは認証され、基本的にサーバーに状態を残さずにアプリケーションにログインします。
そのため、実際にはサーバーは実際にどのユーザーがログインしているかはわかりませんが、ユーザーはログインしていることを知っています。これは、アプリケーションの保護された部分にアクセスするためのパスポートのような有効なJson Webトークンを持っているためです。
もう一度考えてみましょう ユーザーは、サーバーのどこにも保存されていない固有の有効なJson Webトークンを取得するとすぐにログインします。したがって、このプロセスは完全にステートレスです。
次に、たとえばユーザーがユーザープロファイルデータなどの保護されたルートにアクセスするたびに。彼はリクエストとともにJson Webトークンを送信するため、そのルートにアクセスするためにパスポートを表示するのと少し似ています。
リクエストがサーバーに到達すると、アプリはJson Webトークンが実際に有効であるかどうかを確認し、ユーザーが実際に彼の言うとおりの人物である場合、リクエストされたデータがクライアントに送信され、そうでない場合はそこに送信されますそのリソースへのアクセスが許可されていないことをユーザーに通知するエラーになる。
この通信はすべてhttps経由で行う必要があるため、誰でもパスワードやJson Webトークンにアクセスできないように、暗号化されたHttpをセキュリティで保護してください。そうして初めて、本当に安全なシステムが完成します。
したがって、Json Webトークンは、jwt.ioSoのJWTデバッガーから取得されたこのスクリーンショットの左側のように見えます。本質的には、3つの部分で構成されるエンコード文字列です。ヘッダー、ペイロード、および署名これで、ヘッダーはトークン自体に関するメタデータの一部になり、ペイロードは、トークンにエンコードできるデータであり、実際に必要なすべてのデータになります。したがって、ここでエンコードするデータが多いほど、JWTは大きくなります。とにかく、これらの2つの部分はプレーンテキストであり、エンコードされますが暗号化されません。
したがって、誰でもそれらをデコードして読み取ることができるため、機密データをここに保存することはできません。しかし、それはまったく問題ではありません。なぜなら、3番目の部分、つまりシグネチャでは、物事が本当に興味深い場所だからです。署名は、サーバーに保存されているヘッダー、ペイロード、およびシークレットを使用して作成されます。
そして、このプロセス全体がJson Webトークンへの署名と呼ばれます。署名アルゴリズムは、ヘッダー、ペイロード、およびシークレットを使用して、一意の署名を作成します。したがって、このデータとシークレットだけがこの署名を作成できます。よろしいですか?次に、ヘッダーとペイロードとともに、これらの署名はJWTを形成し、それがクライアントに送信されます。
サーバーは、保護されたルートへのアクセスを許可するJWTを受信すると、ユーザーが本当に本人であるかどうかを判断するために、JWTを検証する必要があります。つまり、トークンのヘッダーとペイロードデータを誰も変更していないかどうかを確認します。繰り返しになりますが、この検証手順では、Json Webトークンのヘッダーまたはペイロードのいずれかを第三者が実際に変更していないかどうかを確認します。
では、この検証は実際にはどのように機能しますか?まあ、それは実際には非常に簡単です。JWTが受信されると、検証はヘッダーとペイロードを受け取り、サーバーに保存されているシークレットと共に、基本的にテスト署名を作成します。
しかし、JWTが最初に作成されたときに生成された元の署名はまだトークンに残っていますよね?そして、それがこの検証の鍵です。あとは、テスト署名と元の署名を比較するだけです。また、テスト署名が元の署名と同じ場合は、ペイロードとヘッダーが変更されていないことを意味します。
なぜなら、それらが変更された場合、テスト署名は異なる必要があるからです。したがって、データが変更されていないこの場合は、ユーザーを認証できます。そしてもちろん、2つのシグネチャが実際に異なる場合は、誰かがデータを改ざんしたことを意味します。通常は、ペイロードを変更しようとします。ただし、ペイロードを操作するサードパーティはもちろんシークレットにアクセスできないため、JWTに署名できません。したがって、元の署名は操作されたデータに対応しません。したがって、この場合、検証は常に失敗します。そしてそれが、このシステム全体を機能させるための鍵です。JWTを非常にシンプルにする魔法ですが、非常に強力でもあります。
サーバー上にあるJWTのprivateKeyのみが暗号化されたJWTを復号化します。privateKeyを知っている人は、暗号化されたJWTを復号化できます。
サーバーの安全な場所にあるprivateKeyを非表示にし、privateKeyを誰にも教えないでください。
私と同じように高価なデータベースクエリを利用できない人にとって、機密データ(ユーザー特権など)を保持する1つのオプションは、JWTを生成するときに、このデータを暗号化してJWTトークンにアタッチすることです。(バックエンドに暗号化キーを保持します)
機密情報を読みたい場合は、JWTトークンをバックエンドに送信して復号化し、情報を取り戻すことができます。このようにして、DBルックアップを行ったり、JWTトークンを介してフロントエンドで機密情報を裸にする必要はありません。
復号化するためにjwt.ioに存在しない特別なアルゴリズムを使用してJWEを調べることをお勧めします
参照リンク:https : //www.npmjs.com/package/node-webtokens
jwt.generate('PBES2-HS512+A256KW', 'A256GCM', payload, pwd, (error, token) => {
jwt.parse(token).verify(pwd, (error, parsedToken) => {
// other statements
});
});
この答えは遅すぎるかもしれませんし、あなたはすでに方法を見つけたかもしれませんが、それでも、あなたや他の人にも役立つと思いました。
私が作成した簡単な例:https : //github.com/hansiemithun/jwe-example
md5('original messaged' + secret) != md5('changed message' + secret)
したがって、誰かがメッセージを変更した場合、それを検出することができます