🤔

AWS CognitoのSubが厳密にはUUIDの仕様に準拠していない話

に公開

はじめに

AWSを利用してWebサービスやアプリケーションを開発する際、ユーザー認証やアカウント管理にAWS Cognitoを導入するケースは多いでしょう。特にOAuth 2.0を利用した認証フローでは、Cognitoが発行するユーザー固有のID「sub」が重要な役割を果たします。この「sub」は一見するとUUID(Universally Unique Identifier)のように見えますが、実は厳密な仕様とは異なる点がある、という少しマニアックな話です。

Cognitoで発行される「sub」とは

AWS Cognitoは、OpenID Connect (OIDC) の仕様に準拠しており、すべてのユーザーに標準的な属性セットを割り当てます。
https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims

OIDCはOAuth 2.0を拡張した認証プロトコルであり、OAuth 2.0に対応するCognitoがOIDCの仕様を採用しているのはごく自然なことです。OIDCの仕様では、「sub」は次のように定義されています。

Subject-Identifier for the End-User at the Issuer.

これは発行者(Issuer)におけるエンドユーザーの識別子、つまりユーザーを一意に識別するためのIDを意味します。AWS Cognitoが発行するsubも、ユーザープール内で一意なIDとして保証されています。

そのため、Cognitoを認証基盤として利用する多くのサービスでは、このsubをそのままサービス内のユーザーIDとして扱うのが一般的な設計パターンです。実際にCognitoでユーザーを作成すると、以下のような形式のsubが発行されます。

(画像のユーザー名として表示されているのが、発行されたsubです)

UUIDの仕様

https://developer.mozilla.org/ja/docs/Glossary/UUID

UUIDは、ソフトウェアの世界で広く用いられている汎用一意識別子です。一般的に、128ビットの値を123e4567-e89b-12d3-a456-426614174000のように、ハイフンで5つのブロックに区切られた36文字の16進数文字列として表現されます。

しかし、UUIDの仕様は単なるフォーマットだけでなく、内部構造にもいくつかの重要なルールを定めています。最新の仕様はRFC 9562で定義されています。
https://tex2e.github.io/rfc-translater/html/rfc9562.html

バージョン

UUIDには、その生成方法を示す「バージョン」が定義されています。これは、ハイフンで区切られた3ブロック目の先頭の文字で示されます。

  • 123e4567-e89b-42d3-a456-426614174000

ここの数字がバージョンです。現在よく利用されるのは、ランダムな値に基づくバージョン4や、タイムスタンプベースのバージョン7などです。先ほどのCognitoのsubの例を見ると、この部分が7になっていることから、バージョン7のUUIDを意識して生成されていると考えられます。

バリアント

UUIDの構造には、バージョンに加えて「バリアント」という重要なフィールドがあります。これは、そのUUIDがどの仕様に基づいて生成されたかを示す識別子で、4ブロック目の先頭の文字で示されます。

  • 123e4567-e89b-42d3-a456-426614174000

現在標準とされているRFC 9562準拠のUUIDの場合、このバリアントを示す文字は8, 9, a, bのいずれかである必要があります。

Cognitoで発行されたsubの値の検証

ここで、改めてCognitoが発行したsubの値を見てみましょう。3ブロック目の先頭(バージョン)は7ですが、4ブロック目の先頭(バリアント)は6となっています。

バージョンが7である点は問題ありません。しかし、バリアントが6である点は、RFC 9562が定める標準的なUUIDの仕様と適合しません。そのため、厳密なUUIDバリデーションツールなどにかけると、このsubは無効なUUIDとして判定されることがあります。

何が困るのか?

では、この仕様との不一致は、具体的にどのような問題を引き起こすのでしょうか。

正直なところ、多くのケースでは大きな問題にはならないかもしれません。しかし、システム側で以下のような状況では注意が必要です。

  • 厳密なUUIDバリデーション: システムやデータベースの制約で、ユーザーIDとして受け入れる値を厳密なUUIDフォーマットに限定している場合、Cognitoのsubはバリデーションエラーを引き起こす可能性があります。
  • ライブラリの互換性: UUIDのパースや検証を行うライブラリを使用している場合、実装によってはCognitoのsubを不正な値として扱う可能性があります。これにより、開発環境と本番環境、あるいはライブラリ間での挙動の違いといった問題につながることも考えられます。

まとめ

本記事では、AWS Cognitoが発行するユーザーID「sub」が、一見するとUUIDに見えるものの、厳密にはその仕様に準拠していないケースがある点を紹介しました。

  • 形式: Cognitoのsubは、xxxxxxxx-xxxx-7xxx-xxxx-xxxxxxxxxxxxのようにUUIDバージョン7またはxxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxxのようなUUIDバージョン4に似た形式で発行されることがあります。
  • 問題点: しかし、UUIDの仕様で定められた「バリアント」フィールドの値が標準(8, 9, a, bのいずれか)ではなく、仕様外の値(例:6)を取ることがあります。
  • 影響: このため、厳密なUUIDバリデーションを行うライブラリやシステムでは、Cognitoのsubが不正な値として扱われる可能性があります。

この件について問い合わせをしたところ、AWS側にもUUIDの仕様に沿ったものにしてもらいたいという要望は届いているとのことでした。今後、どこかのタイミングで厳密なUUIDが発行されるようになる可能性はあります。

実用上、subを単なる一意な文字列として扱う限りは現時点でも問題になることは少ないとは思います。詳しく調べてみると、以前から使っているユーザープールではUUIDが仕様に沿ったものを返してくれる場合もあるようで、新しく作成したユーザープールだとUUIDの仕様に厳密には沿っていないという傾向がありそうです。
https://repost.aws/ja/questions/QUfWN5qGq8R5WsocQaP1A9Dg/cognito-sub-attribute-seems-to-be-no-more-a-valid-uuid?sc_ichannel=ha&sc_ilang=en&sc_isite=repost&sc_iplace=hp&sc_icontent=QUfWN5qGq8R5WsocQaP1A9Dg&sc_ipos=8

比較的新しいユーザープールだと問題が発生するという可能性があるので、新しいユーザープールを作成した際には発行されるsubをよくよく確認する必要がありそうです。

コラボスタイル Developers

Discussion