コンテンツにスキップ

CT.S3.PV.4

検証日: 2026-03-22 / リージョン: ap-northeast-1

コントロールの説明

組織外の IAM プリンシパルによる S3 リソースへのアクセスを拒否する。

本コントロールは RCP(リソースコントロールポリシー)により、組織外の IAM プリンシパルからの S3 API 操作をブロックする。Control Tower で対象の OU を指定して有効化すると、その OU 配下の全アカウントの全 S3 リソースに適用される。

RCP テンプレートの Condition は以下の 2 つの条件を AND で評価する。

  • aws:PrincipalIsAWSServicefalse(AWS サービスプリンシパルでない)
  • aws:PrincipalOrgID が自組織の ID と一致しない(組織外のプリンシパルである)

パラメータは以下の 1 つ。

  • ExemptedPrincipalArns(任意): RCP の拒否対象外にする IAM プリンシパルの ARN。組織外のパートナーアカウントや外部サービスのロール等、特定のプリンシパルにアクセスを許可する場合に使用する

本コントロールはバケットポリシーやリソースポリシーを変更するのではなく、独立したアクセス制御レイヤーとして機能する。バケットポリシーで組織外アカウントにアクセスを許可していても、本コントロールが有効な場合は RCP により拒否される。

本コントロール(組織外アクセスの拒否)に直接対応する Security Hub CSPM コントロールは確認できませんでした。

検証の流れ

    flowchart LR
    A[1. 事前準備] --> B[2. 組織内・組織外<br>両方成功]
    B --> C[3. CT.S3.PV.4<br>有効化]
    C --> D[4. 既存設定<br>維持を確認]
    C --> E[5. 組織内アクセス<br>成功を確認]
    C --> F[6. 組織外アクセス<br>拒否を確認]
    D & E & F --> G[7. CT.S3.PV.4<br>無効化]
    G --> H[8. 組織外アクセス<br>成功を確認]
  

結果

  • コントロールの有効化前、組織内・組織外の両方から S3 オブジェクトにアクセスできることを確認できた。
  • コントロールの有効化後、既存のバケットポリシー(組織外アカウントへの許可)は維持されることを確認できた。
  • 組織内プリンシパルからのアクセスは引き続き成功することを確認できた。
  • 組織外プリンシパルからのアクセス(GetObject、PutObject)が拒否されることを確認できた。ただし、エラーメッセージは Access Denied のみで、CT.S3.PV.5CT.SECRETSMANAGER.PV.1 で表示される explicit deny in a resource control policy は含まれなかった(ステップ 6 参照)。
  • コントロールの無効化後、組織外プリンシパルからのアクセスが再び成功することを確認できた。
  • RCP の拒否対象である GetObject / PutObject は CloudTrail のデータイベントに分類されるため、デフォルトでは CloudTrail に記録されない(補足参照)。

1. 事前準備

テスト用の S3 バケットを作成する。

aws s3api create-bucket \
  --bucket ct-s3-pv4-test \
  --create-bucket-configuration LocationConstraint=ap-northeast-1
{
    "Location": "http://ct-s3-pv4-test.s3.amazonaws.com/",
    "BucketArn": "arn:aws:s3:::ct-s3-pv4-test"
}

テスト用オブジェクトをアップロードする。

echo "test data" | aws s3 cp - s3://ct-s3-pv4-test/test.txt
(出力なし)

バケットポリシーで組織外アカウントにアクセスを許可する。

aws s3api put-bucket-policy \
  --bucket ct-s3-pv4-test \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {"AWS": "arn:aws:iam::<組織外アカウント ID>:user/<IAM ユーザー名>"},
        "Action": ["s3:GetObject", "s3:PutObject"],
        "Resource": "arn:aws:s3:::ct-s3-pv4-test/*"
      }
    ]
  }'
(出力なし)

2. 有効化前の確認

組織内アクセス(成功)

aws s3api get-object \
  --bucket ct-s3-pv4-test --key test.txt /dev/null
{
    "AcceptRanges": "bytes",
    "LastModified": "<オブジェクト更新日時>",
    "ContentLength": 10,
    "ETag": "\"39a870a194a787550b6b5d1f49629236\"",
    "ChecksumCRC64NVME": "lAOnrRKrjh8=",
    "ChecksumType": "FULL_OBJECT",
    "ContentType": "binary/octet-stream",
    "ServerSideEncryption": "AES256",
    "Metadata": {}
}

組織外アクセス(成功)

組織外アカウントの認証情報で、バケットポリシーにより許可されたアクセスが成功することを確認する。

aws s3api get-object \
  --bucket ct-s3-pv4-test --key test.txt /dev/null \
  --profile <組織外プロファイル名>
{
    "AcceptRanges": "bytes",
    "LastModified": "<オブジェクト更新日時>",
    "ContentLength": 10,
    "ETag": "\"39a870a194a787550b6b5d1f49629236\"",
    "ChecksumCRC64NVME": "lAOnrRKrjh8=",
    "ChecksumType": "FULL_OBJECT",
    "ContentType": "binary/octet-stream",
    "ServerSideEncryption": "AES256",
    "Metadata": {}
}

バケットポリシーにより、組織外アカウントからもオブジェクトを取得できた。

3. CT.S3.PV.4 の有効化

Control Tower の管理アカウントで、対象の OU にコントロールが有効になっていないことを確認する。

aws controltower list-enabled-controls \
  --target-identifier <OU の ARN> \
  --profile Master
{
    "enabledControls": []
}

コントロールを有効化する。

aws controltower enable-control \
  --control-identifier arn:aws:controlcatalog:::control/7oo9e9bcs8ilm6ekoxu322yb3 \
  --target-identifier <OU の ARN> \
  --profile Master
{
    "arn": "arn:aws:controltower:ap-northeast-1:<管理アカウント ID>:enabledcontrol/<enabledcontrol ID>",
    "operationIdentifier": "<オペレーション ID>"
}

有効化が完了するまで待機する。

aws controltower get-control-operation \
  --operation-identifier <オペレーション ID> \
  --query 'controlOperation.{operationType:operationType,status:status,statusMessage:statusMessage}' \
  --profile Master
{
    "operationType": "ENABLE_CONTROL",
    "status": "SUCCEEDED",
    "statusMessage": "Operation was successful."
}

4. 既存設定の維持確認

RCP はバケットポリシーを変更するのではなく、アクセス制御のレイヤーとして機能する。バケットポリシーが自動的に変更されていないことを確認する。

aws s3api get-bucket-policy \
  --bucket ct-s3-pv4-test \
  --query 'Policy' --output text | jq .
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<組織外アカウント ID>:user/<IAM ユーザー名>"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::ct-s3-pv4-test/*"
        }
    ]
}

バケットポリシーは変更されていない。組織外アカウントへの許可が残っているが、RCP により実際のアクセスは拒否される。

5. 組織内アクセスの確認

組織内プリンシパルからのアクセスは引き続き成功する。

aws s3api get-object \
  --bucket ct-s3-pv4-test --key test.txt /dev/null
{
    "AcceptRanges": "bytes",
    "LastModified": "<オブジェクト更新日時>",
    "ContentLength": 10,
    "ETag": "\"39a870a194a787550b6b5d1f49629236\"",
    "ChecksumCRC64NVME": "lAOnrRKrjh8=",
    "ChecksumType": "FULL_OBJECT",
    "ContentType": "binary/octet-stream",
    "ServerSideEncryption": "AES256",
    "Metadata": {}
}

6. 拒否の確認

組織外 GetObject(拒否)

aws s3api get-object \
  --bucket ct-s3-pv4-test --key test.txt /dev/null \
  --profile <組織外プロファイル名>
An error occurred (AccessDenied) when calling the GetObject operation: Access Denied

組織外 PutObject(拒否)

echo "external test" | aws s3 cp - s3://ct-s3-pv4-test/external.txt \
  --profile <組織外プロファイル名>
upload failed: - to s3://ct-s3-pv4-test/external.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

バケットポリシーで許可されているにもかかわらず、組織外プリンシパルからのアクセスが拒否された。

[CT.S3.PV.5] や [CT.SECRETSMANAGER.PV.1] では explicit deny in a resource control policy を含む詳細なエラーメッセージが返されたが、本コントロールでは Access Denied のみが返された。これは、組織外プリンシパルに対してはセキュリティ上の理由から詳細な拒否理由が開示されないためと推察される(AWS 公式ドキュメントでの確認はできていない)。

7. CT.S3.PV.4 の無効化

aws controltower disable-control \
  --control-identifier arn:aws:controlcatalog:::control/7oo9e9bcs8ilm6ekoxu322yb3 \
  --target-identifier <OU の ARN> \
  --profile Master
{
    "operationIdentifier": "<オペレーション ID>"
}
aws controltower get-control-operation \
  --operation-identifier <オペレーション ID> \
  --query 'controlOperation.{operationType:operationType,status:status,statusMessage:statusMessage}' \
  --profile Master
{
    "operationType": "DISABLE_CONTROL",
    "status": "SUCCEEDED",
    "statusMessage": "Operation was successful."
}

8. 制限解除の確認

コントロール無効化後、組織外プリンシパルからのアクセスが再び成功することを確認する。

aws s3api get-object \
  --bucket ct-s3-pv4-test --key test.txt /dev/null \
  --profile <組織外プロファイル名>
{
    "AcceptRanges": "bytes",
    "LastModified": "<オブジェクト更新日時>",
    "ContentLength": 10,
    "ETag": "\"39a870a194a787550b6b5d1f49629236\"",
    "ChecksumCRC64NVME": "lAOnrRKrjh8=",
    "ChecksumType": "FULL_OBJECT",
    "ContentType": "binary/octet-stream",
    "ServerSideEncryption": "AES256",
    "Metadata": {}
}

組織外プリンシパルからのアクセスが再び成功した。

9. クリーンアップ

aws s3 rm s3://ct-s3-pv4-test/ --recursive
aws s3api delete-bucket-policy --bucket ct-s3-pv4-test
aws s3api delete-bucket --bucket ct-s3-pv4-test

補足: CloudTrail での確認について

RCP による拒否であっても、対象の API が管理イベントに分類される場合は CloudTrail に記録される。例えば、同じ RCP 型の CT.SECRETSMANAGER.PV.1 では GetSecretValue の拒否が管理イベントとして記録され、CloudTrail のイベント履歴や CloudWatch Logs から確認できた。

一方、本コントロールの拒否対象である GetObject / PutObject 等は CloudTrail のデータイベントに分類される。データイベントはデフォルトでは記録されず、確認するには証跡でデータイベントの記録を明示的に有効化する必要がある。

Amazonアソシエイトリンク