Open2
JWTの検証のサンプル
JWTの署名・発行者の検証手順
alg
とkid
を取得
1. JWTのヘッダーからimport base64
import json
def decode_jwt_header(jwt_token):
header_base64 = jwt_token.split('.')[0]
padding = '=' * (4 - len(header_base64) % 4)
header_json = base64.urlsafe_b64decode(header_base64 + padding).decode('utf-8')
header = json.loads(header_json)
return header.get('alg'), header.get('kid')
# 例:
jwt_token = "ヘッダー.ペイロード.署名"
alg, kid = decode_jwt_header(jwt_token)
print(f"alg: {alg}, kid: {kid}")
2. JWKSエンドポイントから公開鍵を取得
import requests
def get_public_key(jwks_url, kid):
response = requests.get(jwks_url)
jwks = response.json()
for key in jwks['keys']:
if key['kid'] == kid:
return key['n'], key['e'], key['alg']
raise ValueError("kidに一致する鍵が見つかりませんでした")
# 例:
jwks_url = "https://example.com/.well-known/jwks.json"
n, e, alg = get_public_key(jwks_url, kid)
3. 公開鍵を生成
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
import base64
def create_rsa_public_key(n, e):
try:
n_int = int.from_bytes(base64.urlsafe_b64decode(n + "=="), 'big')
e_int = int.from_bytes(base64.urlsafe_b64decode(e + "=="), 'big')
public_key = rsa.RSAPublicNumbers(e_int, n_int).public_key(default_backend())
return public_key
except Exception as ex:
print(f"公開鍵の生成に失敗しました: {ex}")
return None
# 例:
public_key = create_rsa_public_key(n, e)
4. JWTの署名を検証
import jwt
from jwt.exceptions import InvalidSignatureError, ExpiredSignatureError, InvalidTokenError
def verify_jwt(jwt_token, public_key):
try:
payload = jwt.decode(jwt_token, public_key, algorithms=['RS256'])
return payload
except InvalidSignatureError:
print("署名が無効です")
except ExpiredSignatureError:
print("JWTの有効期限が切れています")
except InvalidTokenError:
print("トークンが無効です")
return None
# 例:
payload = verify_jwt(jwt_token, public_key)
if payload:
print("署名検証成功:", payload)
else:
print("署名検証失敗")
5. 発行者(iss)を検証
def verify_jwt_and_issuer(jwt_token, public_key, expected_issuer):
try:
# JWTをデコードして署名を検証
payload = jwt.decode(jwt_token, public_key, algorithms=['RS256'])
# `iss`フィールドを取得して検証
issuer = payload.get('iss')
if issuer != expected_issuer:
raise ValueError(f"発行者が一致しません: 期待された発行者: {expected_issuer}, 実際の発行者: {issuer}")
# 署名と発行者が正しければペイロードを返す
return payload
except jwt.InvalidTokenError as e:
print(f"署名検証に失敗しました: {e}")
except ValueError as e:
print(f"発行者の検証に失敗しました: {e}")
return None
# 例: JWTの署名と発行者を検証する
expected_issuer = "https://example.com"
payload = verify_jwt_and_issuer(jwt_token, public_key, expected_issuer)
if payload:
print("署名検証および発行者検証成功:", payload)
else:
print("署名または発行者の検証に失敗しました")
6. まとめ
- JWTのヘッダーから
alg
とkid
を取得 - JWKSエンドポイントから公開鍵情報を取得
- 公開鍵を使ってJWTの署名を検証
- ペイロードの
iss
フィールドを検証
IstioのRequestAuthenticationにおけるissuerとjwksUriも、このような概念でチェックしていると思う。
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -H "Authorization: Bearer invalidToken" -w "%{http_code}\n"
401