0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

S3バケット名は本当に「全世界で一意」なのか?調べたら2026年に常識が変わっていた

0
Posted at

はじめに

「S3のバケット名はグローバルで一意にしないといけない」——AWSを触り始めた頃、誰もが一度は聞かされる話です。私もずっと「全世界で一意」と丸暗記していたのですが、先日ふと「全世界って、本当に地球全体なのか?」と気になって公式ドキュメントを読み込んだところ、思っていたより奥が深く、しかも2026年に常識そのものが更新されていました。

忙しい人向けに、結論を先に置いておきます。

疑問 答え
バケット名は全世界で一意? 正確には「パーティション内」で一意。パーティションは4つある
同じ名前を他人が使っていたら? 同一パーティション内では作れない(早い者勝ち)
削除した名前はどうなる? 誰でも再取得できる。セキュリティ事故の実例あり
回避策は? 2026年3月登場の「アカウントリージョン名前空間」で自分専用の名前が持てる

対象読者: S3を使い始めた初心者〜中級者。AWS認定試験(CLF / SAA)の頻出テーマでもあるので、受験予定の方にも。

この記事のCLI実行結果は、2026年7月に実際に自分のAWSアカウントで検証したものです(アカウントIDはマスクしています)。

参考文献

執筆にあたって参照した一次情報です。まず公式に当たりたい方はこちらからどうぞ。

そもそもなぜバケット名は一意でないといけないのか

理由はシンプルで、バケット名がそのままURLのホスト名に埋め込まれるからです。

S3のオブジェクトにアクセスするとき、現在標準の「virtual-hostedスタイル」ではこういうURLになります。

https://my-bucket.s3.ap-northeast-1.amazonaws.com/photo.jpg

my-bucket の部分がDNSのホスト名の一部になっています。S3はこのホスト名だけを頼りにバケットを特定するので、名前が重複していたら宛先を解決できません。しかもS3には、リージョン情報を含まない古いグローバルエンドポイント(https://my-bucket.s3.amazonaws.com)が後方互換のために今も残っていて、「名前だけでバケットに到達できる」ことが設計の前提になっています。だからこそ、リージョンをまたいだ広い範囲で名前空間を共有する設計になっているわけです。ちなみにバケット名をパス側に置く「パススタイル」(https://s3.ap-northeast-1.amazonaws.com/my-bucket/photo.jpg)も残っていますが、AWSはvirtual-hostedスタイルを推奨しています1

命名ルール自体も、DNSホスト名として成立することが前提になっています。主なものだけ挙げると次のとおりです。

  • 3〜63文字
  • 使えるのは小文字・数字・ピリオド・ハイフンのみ(大文字とアンダースコアは不可)
  • 先頭と末尾は英小文字か数字
  • IPアドレス形式(192.168.5.4 など)は不可
  • xn--sthree- などの予約プレフィックス、-s3alias--x-s3 などの予約サフィックスは使用不可

ピリオド入りの名前(my.example.bucket)は作れますが、HTTPSのvirtual-hostedスタイルで証明書検証が通らなくなるため、静的サイトホスティング以外では避けるのが公式の推奨です。

正確な答えは「パーティション内で一意」

ここが今回いちばん「知らなかった」ポイントでした。公式ドキュメントの記述を正確に読むと、汎用バケットの名前空間は「全世界」ではなく「パーティション内」で共有されています。

パーティションとはリージョンのグループのことで、現在4つあります。

パーティション 対象
aws 標準リージョン(東京・バージニア北部など大半のリージョン)
aws-cn 中国リージョン
aws-us-gov AWS GovCloud (US)
aws-eusc 欧州ソブリンクラウド

s3-namespace-partitions.png

つまり、私が東京リージョンで my-bucket を作っていても、中国リージョン(aws-cn)では別の誰かが同じ名前の my-bucket を作れます。パーティションが違えば名前空間も別だからです。逆に言うと、aws パーティションの中では、東京だろうがバージニアだろうがサンパウロだろうが、全リージョン・全AWSアカウントを横断して早い者勝ちの一発勝負になります。

普段 aws パーティションしか触らない人にとっては「実質グローバルで一意」で困らないのですが、「全世界で一意」は厳密には正しくない——というのが正確な答えです。試験対策としては「グローバルで一意」で覚えて問題ないものの、仕組みまで知っておくと記憶が定着しやすいと思います。

名前の衝突を実際に起こしてみる

ドキュメントを読むだけでは面白くないので、実際に「取られている名前」でバケットを作ろうとしてみました。相手は、世界一取られていそうな名前 test です。

aws s3api create-bucket --bucket test --region ap-northeast-1 --create-bucket-configuration LocationConstraint=ap-northeast-1

結果はこうなりました。

An error occurred (BucketAlreadyExists) when calling the CreateBucket operation:
The requested bucket name is not available. The bucket namespace is shared by
all users of the system. Please select a different name and try again.

「The bucket namespace is shared by all users of the system」——エラーメッセージ自身が仕様を説明してくれています。自分の所有するバケットと同名で作ろうとした場合は、BucketAlreadyExists ではなく BucketAlreadyOwnedByYou という別のエラーになります(例外として、バージニア北部リージョンだけはレガシー互換のためエラーにならず200が返る仕様が残っています)。

ついでに head-bucket でも確認してみます。

aws s3api head-bucket --bucket test
An error occurred (403) when calling the HeadBucket operation: Forbidden

ここで少しつまずきました。403が返ってきた瞬間、「あれ、自分のIAM権限が足りない?」と自分の設定を疑い始めてしまったんです。実はこれは想定内の応答で、head-bucket は名前が存在しなければ404、存在するけれどアクセス権のないバケット(=他人の所有)にはおおむね403を返します。つまりこの403は「その名前はもう誰かに取られていそう」というサインでした。ただし公式のAPI仕様上は「存在しないかアクセスできない場合、汎用的な400・403・404を返す」と定義されているだけなので、403単独を「確実に取られている」の判定に使うのは危険です。それにしても、権限エラーの403を見ると反射的に自分のポリシーを見に行ってしまう人、私だけではないと思います。

削除したバケット名は他人のものになる

一意性の裏返しとして、意外と見落とされがちな仕様があります。バケットを削除すると、その名前は同一パーティション内の全AWSアカウントに開放されるという点です。

公式ドキュメントには、さらに踏み込んだ警告が書かれています。

  • 削除した名前がすぐ再取得できるとは限らず、場合によっては二度と取得できない
  • 他のアカウントがその名前でバケットを再作成すると、旧バケット宛てのリクエストを受け取れてしまう

後者が問題で、アプリケーションの設定にバケット名が残ったままだと、削除後に誰かが同名バケットを作った時点で、あなたのアプリのデータがその誰かのバケットに書き込まれる可能性があります。S3はバケットを「名前」で識別するので、名前の持ち主が変われば宛先も変わってしまうわけです。

このため公式は「名前を使い続けたいバケットは削除しない」ことを推奨しています。使わなくなったバケットは、削除する代わりに中身を空にして保持し、必要ならリクエストをブロックするのが安全です。

名前が予測できると攻撃される — Bucket Monopoly

「削除した名前が取られる」が実際の攻撃に発展した例が、Black Hat USA 2024でAqua Securityが発表した「Bucket Monopoly」です2

CloudFormationやGlue、SageMakerなど6つのAWSサービスには、初回利用時に定型的な名前でS3バケットを自動作成する挙動がありました。GlueやSageMakerなどはアカウントIDとリージョン名だけから名前を予測でき、CloudFormationなどは名前にランダムなハッシュ値を含むものの、その値がどこかに露出していれば同じように特定できます。攻撃者は標的のバケット名を特定できれば、標的がまだ使っていないリージョンに「そのサービスが将来作るはずの名前」でバケットを先回りして作成できます。標的がそのリージョンでサービスを使い始めると、サービスは攻撃者のバケットを自分のものと思い込んで読み書きしてしまい、結果としてリモートコード実行やアカウント乗っ取りまで成立することが実証されました。

これは「バケット名がグローバル(正確にはパーティション内)で共有される」という仕様そのものを突いた攻撃です。教訓として、公式ドキュメントも次の対策を推奨しています。

  • 自動作成するバケット名にはGUIDなど予測不能な文字列を付ける
  • x-amz-expected-bucket-owner(バケット所有者条件)でリクエスト先バケットの所有者を検証する
  • 名前が取れる前提のコードを書かない(取れなかった場合のフォールバックを用意する)

myapp-prod mycompany-logs のような素直で予測しやすい名前、テンプレートに直書きしていませんか? 便利な反面、この攻撃面と地続きだということは頭の片隅に置いておきたいところです。

2026年3月、専用の名前空間がついに登場した

そして本題の「常識が変わった」話です。2026年3月、汎用バケットに「アカウントリージョン名前空間」という選択肢が追加されました3

これは、自分のアカウントだけが使える予約済みの名前空間です。次の命名規則に従うバケット名は、他のどのアカウントにも永久に作成できません。

{任意のプレフィックス}-{12桁のアカウントID}-{リージョンコード}-an

作成するときは CreateBucketx-amz-bucket-namespace: account-regional ヘッダーを付けます。CLIだとこうです。

aws s3api create-bucket --bucket my-app-logs-999999999999-ap-northeast-1-an --bucket-namespace account-regional --region ap-northeast-1 --create-bucket-configuration LocationConstraint=ap-northeast-1

本当に他人が横取りできないのか、試しに自分のものではないアカウントID(ドキュメント用のダミーID)で作ろうとしてみました。

An error occurred (InvalidBucketNamespace) when calling the CreateBucket operation:
The requested bucket is an account-regional namespace bucket, but the requested
AWS Account ID '111122223333' does not match the caller's AWS Account ID
'XXXXXXXXXXXX'. Specify the caller's AWS Account ID in the bucket name.

サフィックスのアカウントIDと呼び出し元のアカウントIDが一致しないと、その場で拒否されます。名前の先取りも、削除後の乗っ取りも、この名前空間では構造的に起こりません。前セクションのBucket Monopolyのような攻撃への根本対策になっているわけです。

利用にあたって、押さえておきたいポイントがいくつかあります。

  • 機能面は通常の汎用バケットと同じで、アプリケーション側の変更は不要
  • IAMポリシーやSCP・RCPで s3:x-amz-bucket-namespace 条件キーを使えば、「アカウントリージョン名前空間以外でのバケット作成を禁止」を組織に強制できる
  • CloudFormationでは BucketNamespace: account-regional の指定とあわせて、疑似パラメータで !Sub "my-bucket-${AWS::AccountId}-${AWS::Region}-an" のように書けるほか、BucketNamePrefix プロパティでサフィックスを自動付与させることもできる
  • サフィックスも63文字制限に含まれる。長さはリージョンコード次第で、たとえば -012345678910-us-east-1-an なら26文字なので、プレフィックスに使えるのは37文字
  • 執筆時点(2026年7月)では中東の一部リージョン(バーレーン・UAE)では利用できない

複数リージョン・複数アカウントに同じ構成を展開するIaCでは、これまで「名前が取られていたらどうする問題」に悩まされてきました。アカウントIDとリージョンをサフィックスに含めるこの形式なら、テンプレートから決定的に名前を生成しても衝突しません。公式もこれをセキュリティベストプラクティスとして推奨しています。

ディレクトリバケットは別ルール

ここまでの話はすべて「汎用バケット(general purpose bucket)」のものです。S3 Express One Zoneで使う「ディレクトリバケット」は、名前空間のルールが最初から別物なので混同注意です。

ディレクトリバケットの名前は {ベース名}--{ゾーンID}--x-s3 という形式(例: my-bucket--apne1-az1--x-s3)で、一意性が要求されるのは選択したアベイラビリティゾーンまたはローカルゾーンの中だけです。グローバルどころか、隣のAZとすら名前空間を共有しません。

ここまでに登場した3種類の名前空間を整理すると、こうなります。

s3-namespace-comparison.png

種類 一意性のスコープ 名前の形式
汎用バケット(共有グローバル) パーティション内・全アカウント 自由(命名規則の範囲で)
汎用バケット(アカウントリージョン) 自分のアカウント専用 {prefix}-{accountId}-{region}-an
ディレクトリバケット 選択したゾーン内 {base}--{zone-id}--x-s3

「S3のバケット名は一意」と一括りに覚えていると、この3つの違いで足をすくわれます。私は今回調べるまで、ディレクトリバケットがゾーン内一意だということを知りませんでした。

まとめ

冒頭の問いに戻ると、「S3のバケット名は全世界・全AWSアカウントで一意でなければいけないのか」の答えはこうなります。

  • 汎用バケットは「パーティション内」で全アカウント横断一意。実用上は「グローバルで一意」の理解で困らないが、厳密には4つのパーティションごとに独立した名前空間がある
  • 削除した名前は他人に渡る。名前を資産として使っているバケットは削除しない
  • 2026年3月以降は、アカウントリージョン名前空間を使えば「名前の早い者勝ち」から降りられる
  • ディレクトリバケットはゾーン内一意でそもそも別ルール

個人的には、20年間「グローバルで一意」だったS3の大前提に公式が風穴を開けたのが素直に面白かったです。新規に作るバケット、特にIaCで量産するバケットは、アカウントリージョン名前空間に寄せていくのが今後の定石になりそうだと感じています。自分も手元のCloudFormationテンプレートから順次 -an サフィックスに移行してみるつもりなので、ハマりどころが見つかったらまた記事にします。

  1. パススタイルは非推奨のレガシー扱いですが、後方互換のため引き続き利用できます。詳細は公式のvirtual hostingのドキュメントを参照してください。

  2. Bucket Monopoly: Breaching AWS Accounts Through Shadow Resources(Aqua Security, 2024)。該当の脆弱性はAWSにより修正済みです。

  3. Amazon S3 introduces account regional namespaces for general purpose buckets(AWS What's New, 2026年3月)

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?