🔐

JWTをハッキングしてみる

2024/12/25に公開

はじめに

こんにちは!
株式会社BTMの畑です。
今回はJWTの取得から、中身のチェック、権限をハッキングして改竄するところまでをやってみます。

そもそもJWTって?

JWTとは「JSON Web Token」の略で、情報をJSON形式で安全にやり取りするための標準規格です。
RFC7519で定義されています。
主にユーザー認証やセッション管理に使用されます。

↓実際のJWTはこのような見た目で、パッと見は何か分からない見た目になっています。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6ImJ0bV9oYXRhIiwiaWF0IjoxMjM0NTY3ODkwfQ.ynJxQmc0Ik0Y9yu7CPwX82Rn0rCVs9i4A8T8x6wLvMA

JWTの構成

JWTは以下の3つのパートで構成されています。

  • ヘッダー (Header)
    • トークンのタイプと使用している署名アルゴリズムを指定
  • ペイロード (Payload)
    • 実際に送信したいデータ(クレーム)を含む
  • 署名 (Signature)
    • トークンが改竄されていないことを確認するための署名

これらの3つのパートはそれぞれBase64でエンコードされ、ドット(.)で区切られて1つの文字列として表現されます。

JWTの一般的な使用場面

  • ユーザー認証: ログイン後のセッション管理に使用され、ユーザーの認証状態を維持します。
  • シングルサインオン(SSO): 複数のサービス間で共通の認証情報を共有する際に利用されます。
  • APIアクセストークン: RESTful APIのアクセス制御やクライアント認証に使用されます。

などなど、Webサービスの様々な場面で利用されています。

JWTの利点

  • ステートレス認証が可能(サーバーでセッション情報を保持する必要がない)
  • スケーラビリティが高い(サーバー間での状態共有が不要)
  • クロスドメインでの認証が容易

ただし、セキュリティ面では適切な実装と運用が重要になります。
署名の検証や有効期限の設定、適切な暗号化アルゴリズムの選択などが必要不可欠です。

JWTをハッキングしてみる

事前準備

簡単に動作確認できるようにPHPの処理を作成しました。

 jwt-demo/
  ├── index.php      # ログイン処理
  ├── admin.php      # 管理ページ
  └── functions.php  # 共通関数

ざっくりと以下の機能を用意しています。

  • ユーザー名とパスワードでログイン認証する。
  • 認証成功時に対象アカウントの権限ロールをJWTで返す。
  • 管理ページにアクセスする際にJWTの権限ロールを確認し、権限があれば表示し、権限がなければ非表示にする。

https://github.com/BTM-GitHub-organization/jwt-demo

まずはJWTを取得します

用意している一般ユーザー(user / user123 )アカウントでログインしてみます。

http://localhost/jwt-demo/index.php

image.png

すると以下のJWTが返ってきました。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqd3QtZGVtbyIsImlhdCI6MTczNDg1OTM5OSwiZXhwIjoxNzM0ODYyOTk5LCJ1c2VybmFtZSI6InVzZXIiLCJyb2xlIjoidXNlciJ9.GDZ5586GkZRLiZwNEBi8O3aRyKBzOsGXmyrBq_bB214

image.png

取得したJWTをjwt.ioでデコードしてみます。

image.png

JWTがデコードされて右側に表示されます。

署名アルゴリズムはHS256が使われて、ペイロードにユーザー名(username)とロール(role)があることが分かります。

このロールを書き換えることができれば、管理者権限のページにアクセスできるかもしれません。

まずはこのJWTのままで、管理者ページにアクセスしてみます。

管理者ページにアクセスしてみる

さっき取得したJWTをリクエストのAuthorizationヘッダーにセットしてPOSTします。

http://localhost/jwt-demo/admin.php

image.png

残念ながら、アクセスできませんでした。

image.png

{"error":"Forbidden"}

では、ペイロードのユーザー名とロールを admin にしてみます。

image.png

jwt.ioのデコード側を書き換えるとエンコード側も自動的に更新されます。

このJWTだとどうでしょうか?

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqd3QtZGVtbyIsImlhdCI6MTczNDg1OTM5OSwiZXhwIjoxNzM0ODYyOTk5LCJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.EPSwhNNLnAKi5aXcBR_F0c4lmA89Y7X5q-lGlXtJeJI

さっきと同じようにJWTをリクエストのAuthorizationヘッダーにセットしてPOSTします。

http://localhost/jwt-demo/admin.php

image.png

{"error":"Forbidden"}

ダメでした。

さすがにそんな簡単じゃないですね。。
「あ、どうも、adminです」と言っていれば、入れてしまう認証だと意味がないので当たり前っちゃ当たり前ですね。

シークレットキーをハッキング

最初にJWTを確認したときに署名アルゴリズムでHS256が使われていることを確認しています。

つまり何かの鍵(シークレットキー)で署名されているので、いくらロールを改竄したとしても、署名検証で不正なJWTと判定されてアクセスできません。

であれば、そのシークレットキーが分かればアクセスできそうですね。

総当たりツール

ここではオープンソースのパスワードクラックツールのJohn the Ripperを利用します。

今回は以下のWindows版を使用しました。

1.9.0-jumbo-1 64-bit Windows binaries

取得したJWTをテキストファイルにコピペして、jwt.txtとして保存します。

作成したjwt.txtを解読対象としてJohn the Ripperを動かします。

PS C:\john-1.9.0-jumbo-1-win64\run> john.exe jwt.txt
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AVX2 8x])
Will run 16 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/run/password.lst, rules:Wordlist
demo             (?)
1g 0:00:00:00 DONE 2/3 (2024-12-23 12:14) 47.61g/s 1560Kp/s 1560Kc/s 1560KC/s 123456..skyline!
Use the "--show" option to display all of the cracked passwords reliably
Session completed

今回は検証用に単純なシークレットキーを設定しているので、あっという間に特定されました。

では、特定されたシークレットキーを入力してJWTを改竄します。

image.png

そして出来上がったJWTをリクエストのAuthorizationヘッダーにセットしてPOSTします。

http://localhost/jwt-demo/admin.php

image.png

{"message":"Welcome to the admin page!"}

やりました!
管理者ページにアクセスできました!

まとめ

JWTを使用する際の注意点

  • 機密情報の扱い
    • ペイロードは暗号化されていないため、機密情報は含めないようにする。
  • セキュアな鍵を設定
  • 有効期限の設定
    • JWTには適切な有効期限を設定し、長期間の使用を避ける。

おわりに

今回はJWTをハッキングしてみることで仕組みを確認しました。
JWTは適切に実装・運用することで安全で効率的な認証・認可の仕組みを提供できます。
セキュリティを確保しながら、モダンなWebアプリケーション開発に活用していきましょう。

Discussion