OIDC / OAuth2 セキュリティ
このページでは、PocketSign Link v2 と連携する RP がブラウザベースの Authorization Code Flow で最低限押さえるべきセキュリティ要点を整理します。
最初に押さえるべきこと
:::warning 必須のセキュリティ要件 ブラウザベースの RP では、少なくとも次をセットで実装してください。これらは安全性に直結する項目であり、省略すると脆弱性の原因になります。
stateを付けてコールバック時に照合するPKCEを使い、code_challenge_method=S256を付けるnonceを付けて、返ってきたid_tokenのnonceを照合するredirect_uriを固定し、認可時とトークン交換時で完全一致させるid_tokenの署名、iss、aud、expを検証する- トークンや認可コードをログに出さない :::
state
state は、サービスが開始した認可リクエストと、後で戻ってくるコールバックを結び付けるための値です。
主な目的は、別のリクエストに対するコールバックを誤って受け入れないようにすることです。
使い方
- 認可開始時にランダムな値を生成する
- サービス側のセッションや一時ストアに保存する
- コールバックで返ってきた
stateと必ず一致確認する - 一致しなければ失敗として扱う
注意点
- 推測しやすい値や連番を使わない
- 1 回使った
stateは再利用しない
PKCE
PKCE は、認可コードを横取りされても、そのコードだけでは token 交換できないようにする仕組みです。
サービスは認可開始時に code_challenge を送り、token 交換時に元の code_verifier を送ります。
PocketSign Link v2 のブラウザフローでは、code_challenge_method は S256 のみサポートします。
流れ
- サービスがランダムな
code_verifierを生成する - サービスが
S256(code_verifier)からcode_challengeを作る authorizeでcode_challengeとcode_challenge_method=S256を送るtokenでcode_verifierを送る- PocketSign Link v2 が両者の対応を検証する
authorize の例
GET /api/oidc/v1/authorize
?client_id=7f3b41e2-a81c-4e35-b08d-2c7e60a4d073
&redirect_uri=https%3A%2F%2Frp.example.com%2Fcallback
&response_type=code
&scope=openid
&state=8f2c...
&nonce=6b11...
&code_challenge=BASE64URL(SHA256(code_verifier))
&code_challenge_method=S256
token の例
curl -X POST "https://id.mock.klon.you/api/oidc/v1/token" \
-u "7f3b41e2-a81c-4e35-b08d-2c7e60a4d073:client-secret" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=SplxlOBeZQQYbYS6WxSbIA" \
-d "redirect_uri=https://rp.example.com/callback" \
-d "code_verifier=random-verifier"
注意点
code_verifierは十分ランダムに生成するcode_verifierは認可コードごとに作り直すplainではなくS256を使う- サーバー側で
code_verifierを安全に保持し、使い終わったら破棄する
nonce
nonce は、id_token がその認可要求に対応するものかを確認するための値です。
特にブラウザフローで id_token を受け取る場合は、nonce を付けて検証するのが安全です。
使い方
- 認可開始時にランダムな
nonceを生成する - サービス側で保存しておく
tokenレスポンスのid_tokenに入っているnonceと照合する- 一致しなければ失敗として扱う
注意点
stateとnonceは目的が違うので、片方だけで済ませないopenidを使ってid_tokenを受け取るならnonceを付ける前提で実装する
redirect_uri
redirect_uri は認可結果の戻り先です。
守ること
- クライアント設定に登録した URI を使う
- 環境ごとに許可した URI だけを明示登録する
クライアント認証
トークエンドポイントなどでは、サーバー間通信でクライアント認証を行います。 認可コードを持っているだけの第三者が、勝手にトークン交換できないようにするためです。
代表的な方式
client_secret_basicclient_secret_postprivate_key_jwt
注意点
client_secretをフロントエンドへ配布しない- シークレットは安全なサーバー側設定に置く
ID トークン検証
id_token は受け取っただけで信用せず、必ず検証してください。
最低限確認する項目
- 署名が
jwks_uriの鍵で検証できること issが想定した PocketSign Link v2 の Issuer であることaudに自分のclient_idが含まれることexpが切れていないことnonceを送った場合は一致すること
追加でよく見る項目
auth_time: 直近認証が必要な画面で確認するacr: 求めた認証強度を満たしているか確認するamr: どの認証手段で認証したか確認するsub: RP 内の利用者識別子として使う
アクセストークンとリフレッシュトークンの扱い
トークンは権限そのものなので、漏えい前提で扱わないことが重要です。
アクセストークン
- HTTPS でのみ送る
- API 呼び出し以外の目的で使わない
- 必要に応じて
introspectで失効確認する
リフレッシュトークン
- サーバー側で安全に保管する
- ローテーション前提で実装する
- ログや監視イベントにそのまま出さない
PAR
authorization_details などで認可リクエストが大きくなる場合や、ブラウザ上での改変可能性を下げたい場合は PAR(Pushed Authorization Requests) を使うと安全です。
PAR の効果
- 認可パラメータ一式をサーバー間で先に確定できる
- ブラウザには
client_idとrequest_uriだけを載せられる - 長いクエリ文字列を避けやすい
注意点
request_uriは使い捨てとして扱うexpires_in内に使うauthorizeでclient_idも一致させる