「JWT」、知っておくべき認証ログインスキーム

JWTの正式名称はJSON Web Tokenです。これは非常に人気のあるクロスドメイン認証ソリューションであり、シングルサインオンシナリオでよく使用されます。

とても使いやすいと思っている方もいらっしゃると思いますが、使った後はサーバー側でredisを使って認証プロセスを実装する必要はありませんが、本質的に欠陥があり使用できないと考える方もいます。

どうしてこれなの?

従来の認証方法

ログインシナリオから話す

これまでに多くのウェブサイトやアプリを使用してきましたが、その多くはログインが必要なため、話をするシーンを選択しましょう。

eコマースシステムを例にとります。注文するには、まずアカウントを登録する必要があります。アカウントを作成したら、ユーザー名(携帯電話番号や電子メールなど)とパスワードを入力して、ログインプロセスを完了する必要があります。一定期間内にシステムに再度アクセスした後は、ユーザー名とパスワードを入力する必要はありません。システムにアクセスするために長期間ログインしていない場合(たとえば、1か月間ログインしていない場合)のみ、ユーザー名とパスワードを再度入力する必要があります。

頻繁に使用するウェブサイトやアプリの場合、通常は長時間パスワードを入力する必要がないため、パソコンや携帯電話を変更した後、頻繁に使用するウェブサイトやアプリのパスワードを思い出せませんアップ。

初期のCookieセッション認証方法

初期のインターネットはWebが主流であり、クライアントはブラウザだったため、初期の段階ではCookie-Session方式が最も一般的に使用されていましたが、これまで、一部のWebサイトではこの方式を認証に使用しています。

認定プロセスはおおよそ次のとおりです。

  1. ユーザーはユーザー名、パスワードを入力するか、SMS確認コードを使用してシステムにログインします。
  2. サーバー認証後、Sessionメッセージを作成し、SessionIDをCookieに保存して、ブラウザに送り返します。
  3. 次回クライアントが要求を開始すると、クライアントは自動的にCookie情報を取得し、サーバーは検証のためにCookieを介してセッション情報を取得します。

「JWT」、知っておくべき認証ログインスキーム

 

しかし、なぜ今では誰もがスマートフォンを使用し、多くの人はコンピューターを使用していないため、従来の認証方法である理由は、TaobaoやPinduoduoなどのさまざまなアプリを携帯で使用しているためです。
この傾向の下で、従来のCookie-Sessionにはいくつかの問題が発生しました:
1.まず、Cookie-SessionはWebシナリオでのみ使用できます。それがAPPの場合、APPがCookieを保存する場所がありません。
現在の製品は基本的にWebとAPPの両方に2つの使用方法を提供しており、一部の製品にはAPPしかありません。

2.一歩下がって、作成した製品がWebのみをサポートし、クロスドメインの問題も考慮する必要があるが、Cookieがドメインをまたがることはできないと言います。
Tmallモールを例にとると、Tmallモールに入ると、上部にTmallスーパーマーケット、Tmallグローバル、Tmallメンバーなどのメニューが表示されます。これらのメニューをクリックすると、異なるドメインに入ります。異なるドメインの下のCookieは異なります。サブドメインであっても、ドメインAの下のドメインBのCookieを取得することはできません。

「JWT」、知っておくべき認証ログインスキーム

 

3.分散サービスの場合、セッション同期を考慮する必要があります。
今日のインターネットのWebサイトとアプリは基本的に分散配置です。つまり、サーバー側に複数のマシンがあります。ユーザーがページでログイン操作を実行する場合、ログインアクションはいずれかのサーバーへの要求である必要があります。ID情報を保存する必要があります。従来の方法は、セッションを保存することです。

次に質問が来ました。複数のページにアクセスしました。現時点では、リクエストは負荷分散され、別のサーバー(ログインしたサーバーではない)にルーティングされます。バックグラウンドでリクエストを受信すると、ユーザーのID情報と権限を確認する必要があるため、インターフェースはセッションからユーザー情報を取得し始めます。ただし、このサーバーはその時点でログインしているサーバーではなく、セッションは保存されないため、バックグラウンドサービスはユーザーをログに記録していないユーザーであると見なし、データを返すことができません。

したがって、この状況を回避するには、セッションの同期を行う必要があります。サーバーがログイン要求を受け取った後、現在のサーバーがセッションを保存した後、他のいくつかのサーバーと同期する必要もあります。

4. CookieにはCSRF(クロスサイトリクエストフォージェリ)のリスクがあります。クロスサイトリクエストフォージェリは、現在ログインしているWebアプリケーションでユーザーが意図しない操作を実行するように強制する攻撃方法です。CSRFは、ユーザーのWebブラウザーでWebサイトの信頼を利用します。簡単に言うと、攻撃者は技術的な手段を使用して、ユーザーのブラウザをだまし、ユーザーが認証したWebサイトにアクセスし、いくつかの操作(商品の購入など)を実行します。ブラウザーが認証されているため、アクセスしたWebサイトは、実際のユーザーが開始した操作と見なされます。
たとえば、私はハッカーで、頻繁にアクセスするテクニカルWebサイトにCSRFの脆弱性を見つけました。投稿記事はhtml形式をサポートしているため、次のような危険なコンテンツをhtmlに追加します

 <img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">

以前にログインしたことがあり、ID情報を識別するCookieが保存されている場合、srcが指すアドレスが通常のショッピングWebサイトの支払いアドレスであると想定します(もちろん、これは単なる例です。実際の攻撃はそれほど単純ではありません)。私が公開した記事をスキャンすると、imgタグが読み込まれるとすぐにCSRF攻撃が機能し、知らないうちにこのWebサイトに支払います。

Cookieセッションの変更バージョン

従来のCookie-Session認証には多くの問題があるため、上記のスキームは変更できます。
1. Cookieの変更CookieはAPPなどの非ブラウザでは使用できないため、クライアント側のストレージにCookieを使用せず、代わりに他の方法を使用してください。
何に変更しますか?
ローカルストレージはウェブで使用でき、クライアントデータベースはAPPで使用できます。これにより、クロスドメインを実現し、CSRFを回避できます。

2.サーバーがセッションを保存しなくなったセッション情報を取り出して、Redisなどのメモリデータベースに保存すると、速度が向上し、セッション同期の問題が回避されます。

変換後、次の認証プロセスになります。

  1. ユーザーはユーザー名、パスワードを入力するか、SMS確認コードを使用してシステムにログインします。
  2. サーバーが検証され、認証情報によって構築されたデータ構造がRedisに格納され、キー値がクライアントに返されます。
  3. クライアントは返されたキーを取得して、ローカルストレージまたはローカルデータベースに格納します。
  4. 次にクライアントが再度要求するときは、キー値をヘッダーまたは要求本文に追加します。
  5. サーバーは、取得したキーに従ってRedisから認証情報を取得します。

次の2つの図は、それぞれ最初のログインと最初以外のログインのプロセスを示しています。

「JWT」、知っておくべき認証ログインスキーム

 

「JWT」、知っておくべき認証ログインスキーム

 

激しい変換の後、従来のCookie-Sessionメソッドの問題が解決されました。この変換は、プロジェクトの開発者が行う必要があります。変革は間違いなく時間がかかり、面倒であり、抜け穴があるかもしれません。

JWTの外観

この時点で、JWTはプレイする準備ができています。JWTはCookie-Session変更バージョンの特定の実装であり、ホイールを自分で作成する時間を節約できます。JWTには別の利点があります。つまり、認証情報をサーバーに保存する必要がない(たとえば、token)はクライアントによって完全に提供され、サーバーはJWT自体が提供する復号化アルゴリズムに従ってユーザーの正当性を検証でき、このプロセスは安全です。

JWTを初めて使用する場合、最も疑わしい点は次のとおりです。JWTがクライアント側(ブラウザ側など)に完全に依存して認証機能を実現でき、すべての認証情報がクライアント側に存在するのはなぜですか。セキュリティを確保する方法は?

JWTデータ構造

JWTの最終的な形式は、区切られた、ヘッダーペイロード、および署名3つの部分で構成される文字列です。次のように:

「JWT」、知っておくべき認証ログインスキーム

 

ヘッダーはJSON形式で表現され、トークンのタイプと暗号化アルゴリズムを示すために使用されます。形式は次のとおりです。つまり、JWT形式が使用され、暗号化アルゴリズムはHS256です。これは最も一般的に使用されるアルゴリズムであり、他にも多くのアルゴリズムがあります。

{
  "alg": "HS256",
  "typ": "JWT"
}

上の図の赤いヘッダー部分に対応して、Base64エンコードが必要です。

負荷

名前、性別、年齢などのユーザー情報など、サーバーが必要とするデータを格納するために使用されます。パスワードなどの重要な機密情報はここに配置しないでください。

{
  "name": "古时的风筝",
  "introduce": "英俊潇洒"
}

さらに、JWTでは、開発者が選択できる7つのフィールドも指定されています。

  • iss(発行者):発行者
  • exp(有効期限):有効期限
  • sub(件名):件名
  • aud(audience):オーディエンス
  • nbf(Not Before):有効時間
  • iat(Issued At):発行時間
  • jti(JWT ID):番号

情報のこの部分もBase64でエンコードする必要があります。

署名

署名には計算式があります。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),  Secret)

HMACSHA256アルゴリズムを使用して計算されるこのメソッドには2つのパラメーターがあります。最初のパラメーターは(base64エンコードされたヘッダー+ base64エンコードされたペイロード)ドットで接続され、後者のパラメーターはカスタム文字列キーです。クライアントに公開しますが、サーバーは知っている必要があります。

使い方

JWTの構造とアルゴリズムを理解した後、それをどのように使用しますか?ここにウェブサイトがあるとします。

1.ユーザーがWebサイトにログインするとき、ユーザー名、パスワード、またはSMS検証を入力してログインする必要があります。ログイン要求がサーバーに到達すると、サーバーはアカウントとパスワードを検証し、JWT文字列を計算してクライアントに返します。

2.クライアントがJWT文字列を取得すると、CookieまたはブラウザのLocalStorageに保存されます。

3.ユーザーにページを設定するように要求するときなど、要求を再送信するか、JWT文字列をHTTP要求ヘッダーに追加するか、要求本文に直接挿入します。

4.サーバーはこのJWT文字列の文字列を取得した後、base64ヘッダーとbase64ペイロードを使用してHMACSHA256アルゴリズムを通じて署名部分を計算し、計算結果が送信された署名部分と一致するかどうかを比較します。一致する場合、リクエストが示されます問題はありませんが、矛盾している場合は、リクエストの期限が切れているか、不正なリクエストです。

「JWT」、知っておくべき認証ログインスキーム

 

「JWT」、知っておくべき認証ログインスキーム

 

安全を確保する方法

セキュリティを確保するための鍵はHMACSHA256または同じタイプの暗号化アルゴリズムで、暗号化プロセスは元に戻せないため、フロントエンドに送信されるJWTに従って鍵情報に復号化することはできません。

また、異なるヘッダーやペイロードを暗号化した後に取得した署名は異なるため、ペイロード部分の情報を誰かが変更した場合、最終的な暗号化結果は変更前のものとは異なるため、最終的な検証結果は不正なリクエストです。

他の人が完全なJWTを取得しても安全ですか?

ペイロードパーツに権限レベルに関連するフィールドが格納されていると仮定すると、強盗は、JWT文字列を取得した後、それをより高い権限レベルに変更する必要があります。前述のように、暗号化された署名が成功しないため、この場合は確実に成功しません。同様に、サーバーは簡単に識別できます。

強盗が変更を行わず、取得した直後にそれを使用する場合、方法はありません。強盗による盗難を大幅に防ぐには、HTTPプロトコルの代わりにHTTPSプロトコルを使用する必要があります。これにより、中間の乗っ取り攻撃を効果的に防止できます。 。

これは安全ではなく、JWT文字列を取得した後、リクエストを簡単にシミュレートできると言う生徒もいます。これは確かにそうですが、前提はあなたがそれをどのように得ることができるかです。上記の中間のハイジャック以外に他の方法はありますか?

強盗があなたのコンピュータを直接奪わない限り、その場合、申し訳ありませんが、JWTが安全でないだけでなく、他のWebサイトや認証方法も安全ではありません。

「JWT」、知っておくべき認証ログインスキーム

 

このようなケースはまれですが、JWTを使用するときは、有効期限をあまり長く設定しないでください。

一つの質問

多くの開発チームがJWTの使用をあきらめる原因となったJWTに問題があり、JWTトークンが発行されると、期限切れにならない限りサーバーはそれを破棄できません。デフォルトでは最新のログインクライアントのみを通常使用できる多くのアプリケーションがあり、マルチターミナルログインは許可されていません。新しいトークンが発行されるため、JWTはそれを実行できませんが、古いトークンは有効期限が切れる前にまだ使用できます。この場合、サーバーは対応するロジックを追加する必要があります。

一般的に使用されるJWTライブラリ

JWTの公式Webサイトには、さまざまな言語に対応するライブラリがリストされています。そのうち、Javaのライブラリは次のとおりです。

「JWT」、知っておくべき認証ログインスキーム

 

例としてjava-jwtを取り上げます。

1.対応するMavenパッケージをインポートします。

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

2.ログインするときに、createメソッドを呼び出してトークンを取得し、それをフロントエンドに返します。

public static String create(){
  try {
    Algorithm algorithm = Algorithm.HMAC256("secret");
    String token = JWT.create()      .withIssuer("auth0")
      .withSubject("subject")
      .withClaim("name","古时的风筝")
      .withClaim("introduce","英俊潇洒")
      .sign(algorithm);    System.out.println(token);
    return token;
  } catch (JWTCreationException exception){
    //Invalid Signing configuration / Couldn't convert Claims.
    throw exception;
  }
}

3.ログインに成功した後、リクエストが再度開始されたときに、トークンをヘッダーまたはリクエスト本文に入れ、サーバーがトークンを確認します。

public static Boolean verify(String token){
  try {
    Algorithm algorithm = Algorithm.HMAC256("secret");
    JWTVerifier verifier = JWT.require(algorithm)
      .withIssuer("auth0")
      .build(); //Reusable verifier instance
    DecodedJWT jwt = verifier.verify(token);    String payload = jwt.getPayload();    String name = jwt.getClaim("name").asString();
    String introduce = jwt.getClaim("introduce").asString();
    System.out.println(payload);    System.out.println(name);    System.out.println(introduce);    return true;
  } catch (JWTVerificationException exception){
    //Invalid signature/claims
    return false;
  }}

4. createメソッドを使用してトークンを生成し、verifyメソッドで確認します。

public static void main(String[] args){
  String token = create();
  Boolean result = verify(token);
  System.out.println(result);}

次の結果を得る

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiaW50cm9kdWNlIjoi6Iux5L-K5r2H5rSSIiwiaXNzIjoiYXV0aDAiLCJuYW1lIjoi5Y-k5pe255qE6aOO562dIn0.ooQ1K_XyljjHf34Nv5iJvg1MQgVe6jlphxv4eeFt8pA
eyJzdWIiOiJzdWJqZWN0IiwiaW50cm9kdWNlIjoi6Iux5L-K5r2H5rSSIiwiaXNzIjoiYXV0aDAiLCJuYW1lIjoi5Y-k5pe255qE6aOO562dIn0
古时的风筝英俊潇洒true

createメソッドを使用して作成されたJWT文字列を検証できます。

また、JWT文字列のペイロード部分と2つのドットの間の部分を変更してから、verifyメソッドを呼び出して検証すると、JWTVerificationExceptionが発生し、検証に合格できません。

おすすめ

転載: blog.csdn.net/GYHYCX/article/details/108779217