一覧SP-11-1 · IdP:認証とアクセス条件

認証と端末状態に基づくアクセス制御

認証結果の検証と、アプリ側の許可条件を順番に確認します。

説明

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

1
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'}
2
claims=verify(t,120) print(claims['sub'],claims['aud']) print(verify(issue(aud='other-app'),120))
u7 portal
recipient_rejected
3
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
4
print(policy(claims,False,120)) print(policy(claims,True,120))
device_rejected
step_up
5
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
6
print(policy(strong,True,180)) print(policy(strong,True,181))
allow
reauthenticate
7
print(isinstance(verify(t,199),dict)) print(verify(t,200))
True
expired

演習完了