認証と端末状態に基づくアクセス制御
認証結果の検証と、アプリ側の許可条件を順番に確認します。
説明
IdPは利用者を認証し、その結果をアプリへ渡します。アプリでは署名、発行者、受信先などを検証した上で、許可する操作を判断します。認証の成功だけで全操作が許可されるわけではありません。
公開試験鍵で作ったJWTの中身と署名を調べ、端末条件、追加認証、認証からの経過時間を変えます。固定のHS256試料であり、OIDCの通信手順全体は対象に含めません。
認証結果の検証と、アプリ側の許可条件を順番に確認します。
1JWTの構成を開く
トークンはヘッダー、クレーム、署名の3部分です。
from sp11 import *
t=issue()
print(len(t.split('.')))
print(decJWTの構成を開く(t.split('.')[0]))
print(dec(t.split('.')[1]))audはportal、subはu7です。Base64urlを復号しただけでは改変の検出はまだ済んでいません。
2署名と受信先を検証する
現在時刻を120とします。時刻値は比較用の固定値です。
claims=verify署名と受信先を検証する(t,120)
print(claims['sub'],claims['aud'])
print(verify(issue(aud='other-app'),120))正しい受信先ではu7、別アプリ宛てではrecipient_rejectedです。署名が正しくても自分宛てとは限りません。
3改変と要求の取り違えを拒否する
署名を付け直さずsubを変え、別nonceも試します。
h,p,sig=t.split('.')
changed=dec(p);changed['sub']='admin'
print(verify改変と要求の取り違えを拒否する(h+'.'+enc(changed)+'.'+sig,120))
print(verify(t,120,nonce='n8'))signature_invalidとnonce_rejectedです。nonceは開始した認証要求と結果を対応付ける値です。
4管理対象の端末かを確認する
署名検証後の結果でも端末条件を評価します。
print(policy管理対象の端末かを確認する(claims,False,120))
print(policy(claims,True,120))管理外端末は拒否、管理対象端末は追加認証待ちです。今回の規則はpwdとotpの両方を必要とします。
5追加認証後の結果を受け取る
新しい認証結果にはotpと更新したauth_timeを含めます。
strong=verify(issue(amr=['pwd','otp'],auth_time=120),130)
print(strong['amr'],strong['auth_time'])
print(policy追加認証後の結果を受け取る(strong,True,130))allowになります。amrは行った認証方法の情報であり、利用者が自分で書き換えた値を採用するものではありません。
6認証からの経過時間で再確認する
有効期限前でも重要操作に必要な認証の新しさを調べます。
print(policy認証からの経過時間で再確認する(strong,True,180))
print(policy(strong,True,181))60秒まではallow、61秒でreauthenticateです。トークンの寿命と認証の新しさは別条件です。
7有効期限の境界を確認する
exp 200と同じ時刻では期限切れです。
print(isinstance(verify有効期限の境界を確認する(t,199),dict))
print(verify(t,200))199は有効、200はexpiredです。許容する時刻ずれを設ける場合も、規則として明示して検証します。
まとめ
内容の復号と署名検証は別の処理です。
受信先・nonce・期限を確認してから許可条件を評価します。
端末条件、認証方法、認証時刻を組み合わせます。
操作履歴と解説を振り返れます。
Python 3.15 / 固定ID・認可条件・SQLite
from sp11 import *
t=issue()
print(len(t.split('.')))
print(dec(t.split('.')[0]))
print(dec(t.split('.')[1]))3
{'alg': 'HS256', 'typ': 'JWT'}
{'iss': 'https://idp.example.test', 'sub': 'u7', 'aud': 'portal', 'iat': 100, 'exp': 200, 'auth_time': 100, 'amr': ['pwd'], 'nonce': 'n7'}claims=verify(t,120)
print(claims['sub'],claims['aud'])
print(verify(issue(aud='other-app'),120))u7 portal recipient_rejected
h,p,sig=t.split('.')
changed=dec(p);changed['sub']='admin'
print(verify(h+'.'+enc(changed)+'.'+sig,120))
print(verify(t,120,nonce='n8'))signature_invalid nonce_rejected
print(policy(claims,False,120))
print(policy(claims,True,120))device_rejected step_up
strong=verify(issue(amr=['pwd','otp'],auth_time=120),130)
print(strong['amr'],strong['auth_time'])
print(policy(strong,True,130))['pwd', 'otp'] 120 allow
print(policy(strong,True,180))
print(policy(strong,True,181))allow reauthenticate
print(isinstance(verify(t,199),dict))
print(verify(t,200))True expired
演習完了