はじめに
こんにちは!
株式会社BTMの畑です。
今回はJWTの取得から、中身のチェック、権限をハッキングして改竄するところまでをやってみます。
本記事で紹介する手法は、セキュリティ検証を目的として自分自身が管理するWebシステムに対してのみ実施してください。
他者のシステムや許可を得ていない環境に対して行うことは不正アクセス禁止法等の法律に違反し、犯罪となります。絶対に行わないでください。
そもそも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の権限ロールを確認し、権限があれば表示し、権限がなければ非表示にする。
今回の確認用に最低限の実装となっています。そのまま利用すると脆弱性等のリスクがありますのでご注意ください。
まずはJWTを取得します
用意している一般ユーザー(user / user123 )アカウントでログインしてみます。
http://localhost/jwt-demo/index.php
すると以下のJWTが返ってきました。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqd3QtZGVtbyIsImlhdCI6MTczNDg1OTM5OSwiZXhwIjoxNzM0ODYyOTk5LCJ1c2VybmFtZSI6InVzZXIiLCJyb2xlIjoidXNlciJ9.GDZ5586GkZRLiZwNEBi8O3aRyKBzOsGXmyrBq_bB214
取得したJWTをjwt.ioでデコードしてみます。
JWTがデコードされて右側に表示されます。
署名アルゴリズムはHS256
が使われて、ペイロードにユーザー名(username)とロール(role)があることが分かります。
このロールを書き換えることができれば、管理者権限のページにアクセスできるかもしれません。
まずはこのJWTのままで、管理者ページにアクセスしてみます。
管理者ページにアクセスしてみる
さっき取得したJWTをリクエストのAuthorizationヘッダーにセットしてPOSTします。
http://localhost/jwt-demo/admin.php
残念ながら、アクセスできませんでした。
{"error":"Forbidden"}
では、ペイロードのユーザー名とロールを admin
にしてみます。
jwt.ioのデコード側を書き換えるとエンコード側も自動的に更新されます。
このJWTだとどうでしょうか?
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqd3QtZGVtbyIsImlhdCI6MTczNDg1OTM5OSwiZXhwIjoxNzM0ODYyOTk5LCJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.EPSwhNNLnAKi5aXcBR_F0c4lmA89Y7X5q-lGlXtJeJI
さっきと同じようにJWTをリクエストのAuthorizationヘッダーにセットしてPOSTします。
http://localhost/jwt-demo/admin.php
{"error":"Forbidden"}
ダメでした。
さすがにそんな簡単じゃないですね。。
「あ、どうも、adminです」と言っていれば、入れてしまう認証だと意味がないので当たり前っちゃ当たり前ですね。
シークレットキーをハッキング
最初にJWTを確認したときに署名アルゴリズムでHS256
が使われていることを確認しています。
つまり何かの鍵(シークレットキー)で署名されているので、いくらロールを改竄したとしても、署名検証で不正なJWTと判定されてアクセスできません。
であれば、そのシークレットキーが分かればアクセスできそうですね。
総当たりツール
ここではオープンソースのパスワードクラックツールのJohn the Ripperを利用します。
パスワード管理やセキュリティを考える際に、攻撃者がどのようにパスワードを解読するかを知っておくことは重要です。
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
今回は検証用に単純なシークレットキーを設定しているので、あっという間に特定されました。
AIによるパスワード解析も発展しており日々解析速度が速くなっています。
複雑で長いキーにすることと合わせて、JWTの有効期限を適切に設定することも重要です。
参考:AIによるパスワードの解析時間をセキュリティ会社が公開。8桁なら7時間以内に解析完了
では、特定されたシークレットキーを入力してJWTを改竄します。
そして出来上がったJWTをリクエストのAuthorizationヘッダーにセットしてPOSTします。
http://localhost/jwt-demo/admin.php
{"message":"Welcome to the admin page!"}
やりました!
管理者ページにアクセスできました!
まとめ
JWTを使用する際の注意点
-
機密情報の扱い
- ペイロードは暗号化されていないため、機密情報は含めないようにする。
-
セキュアな鍵を設定
- ランダムで長いシークレットキーを使用する。(32文字以上推奨)
-
有効期限の設定
- JWTには適切な有効期限を設定し、長期間の使用を避ける。
おわりに
今回はJWTをハッキングしてみることで仕組みを確認しました。
JWTは適切に実装・運用することで安全で効率的な認証・認可の仕組みを提供できます。
セキュリティを確保しながら、モダンなWebアプリケーション開発に活用していきましょう。
株式会社BTMではエンジニアの採用をしております。
ご興味がある方はぜひコチラをご覧ください。