0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マネージドログインのAuthorization Code Flow with PKCEを覗いてみた。

Last updated at Posted at 2025-04-16

はじめに

下記リンクの通り、CognitoはOIDCの認証フローの中で認可コードを発行する処理にPKCEが利用できます。

ちょうど、情報処理安全支援確保士試験の勉強しているので、実際の通信を覗いてみたいと思います。

PKCE(ピクシー)とは?

SPAのようなクライアントサイドで動作するWEBやアプリは認証コードの横取りを防ぐ必要があり、この仕組みがPKCEになります。

詳細は以下サイトをご確認ください。

通信を覗いてみる。

認証開始時

マネージドログイン画面を表示したタイミングで処理開始かな?と思ってたのですが、実際はID(今回はメールアドレス)を入れた時に認証が開始されます。

スクリーンショット 2025-04-17 3.55.51.png

クエリパラメタは以下の通りです。

login?client_id=ar4sjg7u1g1t16cah2rjfkih3&code_challenge=V11qZ0ganE__op3krG3POUEYb5AV_-KiK_vRTordda4&code_challenge_method=S256&identity_provider=COGNITO&lang=ja&redirect_uri=https%3A%2F%2Fstaging.d3prbb8vtbfpjg.amplifyapp.com%2F&response_type=code&scope=email+openid+aws.cognito.signin.user.admin+profile&state=zARVByIx0HRLOde7n7I9LlaTAGyIfIcH&_data=routes%2Flogin

一部抜粋してまとめてみます。

パラメータ名 説明
code_challenge V11qZ0ganE__op3krG3POUEYb5AV_-KiK_vRTordda4 PKCE認証用のコードチャレンジ
code_challenge_method S256 コードチャレンジに使用されるハッシュメソッド (SHA-256)
redirect_uri https://staging.xxxxxxxxxx.amplifyapp.com/ 認証後のリダイレクト先URL
response_type code 認証レスポンスタイプ
scope email openid aws.cognito.signin.user.admin profile 要求されるアクセス権限の範囲

code_challengeは、毎回生成されるランダムな値code_verifycode_challenge_methodのアルゴルムでハッシュ化した値です。

今時点でCognitoが分かるのは、何かしらの値をS256でハッシュ化した値が、V11qZ0ganE__op3krG3POUEYb5AV_-KiK_vRTordda4であることです。

つまり、毎回生成されるランダムな値code_verifierを知っているクライアントが認証を開始したクライアントである、ということになります。

パスワード入力後

次にパスワードを入力すると、verifyPasswordが3回呼ばれていました。

複数呼ばれることはReact等の仕様の模様です。

image.png

同様にクエリパラメタを確認します。

verifyPassword?client_id=ar4sjg7u1g1t16cah2rjfkih3&code_challenge=V11qZ0ganE__op3krG3POUEYb5AV_-KiK_vRTordda4&code_challenge_method=S256&lang=ja&redirect_uri=https%3A%2F%2Fstaging.d3prbb8vtbfpjg.amplifyapp.com%2F&response_type=code&scope=email+openid+aws.cognito.signin.user.admin+profile&state=zARVByIx0HRLOde7n7I9LlaTAGyIfIcH&_data=root

特にパラメタ変更はありません。

パラメータ名 説明
code_challenge V11qZ0ganE__op3krG3POUEYb5AV_-KiK_vRTordda4 PKCE認証用のコードチャレンジ
code_challenge_method S256 コードチャレンジに使用されるハッシュメソッド (SHA-256)
redirect_uri https://staging.xxxxxxxxxx.amplifyapp.com/ 認証後のリダイレクト先URL
response_type code 認証レスポンスタイプ
scope email openid aws.cognito.signin.user.admin profile 要求されるアクセス権限の範囲

認証成功後のリダイレクト

ログインに成功すると、redirect_uriに指定していたURIにリダイレクトされますが、このクエリパラメタに認可コードが付与されます。

https://staging.xxxxxxxxxx.amplifyapp.com/?code=2baa4995-88b8-44ed-b7bc-d0d894336ded&state=zARVByIx0HRLOde7n7I9LlaTAGyIfIcH
パラメータ名 説明
code 2baa4995-88b8-44ed-b7bc-d0d894336ded 認可コード

認可コードは一度限り利用できるコードで、次のトークン取得で即座に消費されるます。

ここで悪意のあるツールを利用し、他者が利用可能な認可コードを奪うことを「認可コード横取り」と言います。

トークン取得

最後に認可コードから各種トークンを取得します。

https://ap-northeast-1nn5ct2qua.auth.ap-northeast-1.amazoncognito.com/oauth2/token

POST通信なので、リクエストボディは以下の通り。

grant_type=authorization_code&code=2baa4995-88b8-44ed-b7bc-d0d894336ded&client_id=ar4sjg7u1g1t16cah2rjfkih3&redirect_uri=https%3A%2F%2Fstaging.xxxxxxxxxx.amplifyapp.com%2F&code_verifier=3JLGEyr6ExmJNTWxKGWeWOcErTkhLh4DDz2pOBVDAbpSr1Dxe2yx0esP7l7qq2IZSjiA2JfngPVk0V4RBrRvzw6eCiHAdcMLFOqfCpi0dgcHeYaBOtoIfGLQsdswCwyH

抜粋して表にします。

パラメータ名 説明
grant_type authorization_code トークン交換のタイプ(認可コードフロー)
code 2baa4995-88b8-44ed-b7bc-d0d894336ded 認証後に取得した認可コード
client_id ar4sjg7u1g1t16cah2rjfkih3 アプリケーションのクライアントID
redirect_uri https://staging.xxxxxxxxxx.amplifyapp.com/ 認可コード取得時と同じリダイレクトURI
code_verifier 3JLGEyr6ExmJNTWxKGWeWOcErTkhLh4DDz2pOBVDAbpSr1Dxe2yx0esP7l7qq2IZSjiA2JfngPVk0V4RBrRvzw6eCiHAdcMLFOqfCpi0dgcHeYaBOtoIfGLQsdswCwyH PKCE用のコード検証子

ここでcode_verifierが出てきました。

つまり、認可コードから各種トークンを取得できるクライアントは、ハッシュ化前のランダムな文字列code_verifierを得るクライアント(=認証開始時のクライアント)のみです。

これで認可コードを利用するクライアントは、認証を要求してきたクライアントであることを担保できます。

何かしらの方法で認可コードを横取りしても、code_verifierが分からないと各種トークンを取得できないのです。

なお、認可コード自体は各種トークンを取得する以外用途もありません。

無事取得に成功すると、以下の通りトークン取得できます。

{
    "id_token": "[IDトークンの値]",
    "access_token": "[アクセストークンの値]",
    "refresh_token": "[リフレッシュトークンの値]",
    "expires_in": 3600,
    "token_type": "Bearer"
}

値検証してみた。

最後に認証サービスがどうやって値の検証をしているかを確認してみました。

ロジックは以下のようです。

  1. code_verifierをUTF-8エンコードしてバイト配列に変換
  2. そのバイト配列のSHA-256ハッシュを計算(これはバイナリデータ)
  3. ハッシュのバイナリデータをBase64エンコード
  4. Base64をBase64URLに変換(「+」→「-」、「/」→「_」、末尾の「=」を削除)

Pythonコードにしてみました。

import hashlib
import base64

def generate_code_challenge(code_verifier):
    # code_verifierをバイト列に変換
    verifier_bytes = code_verifier.encode('utf-8')
    
    # SHA-256ハッシュを計算
    hash_object = hashlib.sha256(verifier_bytes)
    hash_digest = hash_object.digest()
    
    # Base64エンコード
    base64_encoded = base64.b64encode(hash_digest)
    
    # Base64をBase64URL形式に変換(+ → -、/ → _、末尾の = を削除)
    base64_url = base64_encoded.decode('utf-8').replace('+', '-').replace('/', '_').rstrip('=')
    
    return base64_url

# 実行例
code_verifier = "3JLGEyr6ExmJNTWxKGWeWOcErTkhLh4DDz2pOBVDAbpSr1Dxe2yx0esP7l7qq2IZSjiA2JfngPVk0V4RBrRvzw6eCiHAdcMLFOqfCpi0dgcHeYaBOtoIfGLQsdswCwyH"
code_challenge = generate_code_challenge(code_verifier)
print(f"code_verifier: {code_verifier}")
print(f"code_challenge_method: S256")
print(f"code_challenge: {code_challenge}")

実行結果です。

無事、code_verifierから認証開始時のcode_challengeが生成できています。

スクリーンショット 2025-04-17 4.49.26.png

最後に

実際に通信まで見ると理解が深まって良いですね〜!

試験で出てくれるといいな。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?