コンテンツにスキップ

CT.S3.PV.5

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

コントロールの説明

S3 バケットへの HTTP(非暗号化)アクセスを禁止する。

本コントロールは RCP(リソースコントロールポリシー)により、S3 バケットへの HTTP アクセスをブロックする。Control Tower で対象の OU を指定して有効化すると、その OU 配下の全アカウントの全 S3 バケットに適用される。各バケットにバケットポリシーを設定する必要はない。ただし、本コントロールはバケットポリシーを自動追加するわけではないため、Security Hub [S3.5] は引き続き FAILED として検出される。

RCP テンプレートの Condition は aws:SecureTransportfalse の場合に S3 API 操作を拒否する。つまり、HTTP(非暗号化)でのリクエストがブロックされ、HTTPS でのリクエストは許可される。

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

  • ExemptedPrincipalArns(任意): RCP の拒否対象外にする IAM プリンシパルの ARN。レガシーシステムの IAM ロールがすぐに HTTPS へ移行できない場合に、移行期間中の互換性を維持するために使用する

S3 の HTTPS 強制には以下の方法がある。

方法内容スコープ
Security Hub S3.5バケットポリシーに HTTPS 強制条件があるかチェック検出のみ(ブロックしない)
バケットポリシー(手動)バケットごとに HTTP を拒否するポリシーを設定(補足1参照バケット単位
CT.S3.PV.5(本コントロール)OU 配下の全バケットで HTTP を自動的に拒否OU 単位

Security Hub [S3.5] はバケットポリシーの設定有無をチェックするだけで、HTTP アクセス自体はブロックしない。バケットポリシーによる手動設定はバケットごとに必要で、設定漏れのリスクがある。

検証環境

検証環境の構成図

本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。事前に export AWS_PROFILE=Workload でデフォルトを設定しておくと便利。

検証の流れ

    flowchart LR
    A[1. 事前準備] --> B[2. HTTP/HTTPS<br>両方成功]
    B --> C[3. Security Hub S3.5<br>で検出を確認]
    C --> D[4. CT.S3.PV.5<br>有効化]
    D --> E[5. 既存設定<br>維持を確認]
    D --> F[6. Security Hub S3.5<br>FAILED のまま]
    D --> G[7. HTTP 拒否<br>HTTPS 成功を確認]
    E & F & G --> H[8. CT.S3.PV.5<br>無効化]
    H --> I[9. HTTP アクセス<br>成功を確認]
  

結果

  • コントロールの有効化前、HTTP・HTTPS の両方で S3 オブジェクトにアクセスできることを確認できた。
  • Security Hub [S3.5] で「FAILED」として検出されることを確認できた。
  • コントロールの有効化後、既存のバケット設定(バケットポリシーなし)は維持されることを確認できた。
  • コントロール有効化後も Security Hub [S3.5] は FAILED のまま変化しなかった。RCP はバケットポリシーを追加するわけではないため、S3.5 の評価結果は変わらない。CT.EC2.PV.7(Declarative Policy)で Security Hub が PASSED に変化する動作とは対照的である。
  • HTTP でのアクセス(GetObject、PutObject)が explicit deny in a resource control policy で拒否されることを確認できた。
  • HTTPS でのアクセスは正常に動作することを確認できた。
  • コントロールの無効化後、再び HTTP アクセスが可能になることを確認できた。
  • RCP の拒否対象である GetObject / PutObject は CloudTrail のデータイベントに分類されるため、デフォルトでは CloudTrail に記録されない。管理イベントとして記録される他コントロール(CT.SECRETSMANAGER.PV.1CT.KMS.PV.7 等)とは異なる(補足2参照)。

1. 事前準備

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

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

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

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

2. コントロール有効化前の確認

コントロール有効化前は、HTTP・HTTPS の両方でアクセスできることを確認する。

HTTPS でのアクセス(成功)

aws s3api get-object \
  --bucket ct-s3-pv5-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": {}
}

HTTP でのアクセス(成功)

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

HTTP でも正常にオブジェクトを取得できた。

3. Security Hub S3.5 での検出確認

Security Hub の [S3.5] コントロールで、テスト用の S3 バケットのバケットポリシーに SSL 強制条件がないことが検出されているか確認する。

aws securityhub get-findings \
  --filters '{
    "GeneratorId": [{"Value": "security-control/S3.5", "Comparison": "PREFIX"}],
    "ResourceId": [{"Value": "arn:aws:s3:::ct-s3-pv5-test", "Comparison": "EQUALS"}]
  }' \
  --query 'Findings[0].{Compliance:Compliance.Status,Title:Title}'
{
    "Compliance": "FAILED",
    "Title": "S3 general purpose buckets should require requests to use SSL"
}

バケットポリシーに aws:SecureTransport 条件がないため、FAILED として検出されている。

Security Hub の検出はスケジュールベースのため、バケット作成直後には反映されない場合があります。すぐに確認したい場合は、対応する Config ルールの評価を手動トリガーしてください(CT.EC2.PV.3 の補足と同様の手順で、S3.5 に対応する Config ルールを指定します)。

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

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/7mo7a2h2ebsq71l8k6uzr96ou \
  --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."
}

5. 既存設定の維持確認

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

aws s3api get-bucket-policy \
  --bucket ct-s3-pv5-test
An error occurred (NoSuchBucketPolicy) when calling the GetBucketPolicy operation: The bucket policy does not exist

バケットポリシーは追加されていない。RCP はバケットの設定を変更せず、独立したアクセス制御レイヤーとして HTTP アクセスをブロックする。

6. Security Hub S3.5 が FAILED のまま変化しないことの確認

RCP はバケットポリシーを追加するわけではないため、コントロール有効化後も Security Hub [S3.5] は FAILED のまま変化しない。

aws securityhub get-findings \
  --filters '{
    "GeneratorId": [{"Value": "security-control/S3.5", "Comparison": "PREFIX"}],
    "ResourceId": [{"Value": "arn:aws:s3:::ct-s3-pv5-test", "Comparison": "EQUALS"}]
  }' \
  --query 'Findings[0].{Compliance:Compliance.Status,Title:Title}'
{
    "Compliance": "FAILED",
    "Title": "S3 general purpose buckets should require requests to use SSL"
}

FAILED のまま変化していない。CT.EC2.PV.7(Declarative Policy)では Block Public Access の強制により Security Hub が PASSED に変化したが、RCP はバケットの設定自体を変更しないため、S3.5 の評価結果は変わらない。

7. 拒否の確認

HTTP GetObject(拒否)

aws s3api get-object \
  --bucket ct-s3-pv5-test --key test.txt /dev/null \
  --endpoint-url http://s3.ap-northeast-1.amazonaws.com
An error occurred (AccessDenied) when calling the GetObject operation:
User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<セッション名>
is not authorized to perform: s3:GetObject on resource:
"arn:aws:s3:::ct-s3-pv5-test/test.txt" with an explicit deny in a resource control policy

HTTP PutObject(拒否)

echo "test" | aws s3 cp - s3://ct-s3-pv5-test/test2.txt \
  --endpoint-url http://s3.ap-northeast-1.amazonaws.com
upload failed: - to s3://ct-s3-pv5-test/test2.txt An error occurred (AccessDenied) when calling the PutObject operation:
User: arn:aws:sts::<Workload のアカウント ID>:assumed-role/<ロール名>/<セッション名>
is not authorized to perform: s3:PutObject on resource:
"arn:aws:s3:::ct-s3-pv5-test/test2.txt" with an explicit deny in a resource control policy

HTTPS GetObject(成功)

aws s3api get-object \
  --bucket ct-s3-pv5-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": {}
}

HTTPS PutObject(成功)

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

HTTP アクセスのみが拒否され、HTTPS アクセスは正常に動作している。エラーメッセージに explicit deny in a resource control policy と記載されており、RCP による拒否であることがわかる。

8. CT.S3.PV.5 の無効化

aws controltower disable-control \
  --control-identifier arn:aws:controlcatalog:::control/7mo7a2h2ebsq71l8k6uzr96ou \
  --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."
}

9. 制限解除の確認

コントロール無効化後、HTTP アクセスが再び成功することを確認する。

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

HTTP アクセスが再び成功した。コントロールを無効化すると即座に制限が解除される。

10. クリーンアップ

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

補足1: バケットポリシーによる SSL 強制との比較

CT.S3.PV.5 を使わずにバケットポリシーで HTTP を拒否する場合は、以下のようなポリシーをバケットごとに設定する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::<バケット名>",
                "arn:aws:s3:::<バケット名>/*"
            ],
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": "false"
                }
            }
        }
    ]
}

バケットポリシーで拒否された場合と CT.S3.PV.5(RCP)で拒否された場合では、エラーメッセージが異なる。

拒否元エラーメッセージ
RCP(CT.S3.PV.5)explicit deny in a resource control policy
バケットポリシーexplicit deny in a resource-based policy

この違いにより、HTTP アクセスが拒否された際にどのレイヤーで拒否されたかを特定できる。

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

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

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

以下の手順で、RCP による拒否がデータイベントとして記録されることを確認できる。

データイベント記録用の証跡を作成

ログ配信先バケットを作成し、CloudTrail サービスプリンシパルに書き込みを許可する。

aws s3api create-bucket \
  --bucket <ログ配信先バケット名> \
  --create-bucket-configuration LocationConstraint=ap-northeast-1
aws s3api put-bucket-policy \
  --bucket <ログ配信先バケット名> \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AWSCloudTrailAclCheck",
        "Effect": "Allow",
        "Principal": {"Service": "cloudtrail.amazonaws.com"},
        "Action": "s3:GetBucketAcl",
        "Resource": "arn:aws:s3:::<ログ配信先バケット名>"
      },
      {
        "Sid": "AWSCloudTrailWrite",
        "Effect": "Allow",
        "Principal": {"Service": "cloudtrail.amazonaws.com"},
        "Action": "s3:PutObject",
        "Resource": "arn:aws:s3:::<ログ配信先バケット名>/AWSLogs/<Workload のアカウント ID>/*",
        "Condition": {"StringEquals": {"s3:x-amz-acl": "bucket-owner-full-control"}}
      }
    ]
  }'
(出力なし)

証跡を作成し、対象バケットの S3 データイベントのみを記録するよう設定する。

aws cloudtrail create-trail \
  --name <証跡名> \
  --s3-bucket-name <ログ配信先バケット名>
aws cloudtrail put-event-selectors \
  --trail-name <証跡名> \
  --event-selectors '[{
    "ReadWriteType": "All",
    "IncludeManagementEvents": false,
    "DataResources": [{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::<テスト用バケット名>/"]}]
  }]'
aws cloudtrail start-logging \
  --name <証跡名>
(出力なし)

拒否イベントの確認

CT.S3.PV.5 有効化中に HTTP で GetObject を実行して拒否された後、5〜15 分程度待つとログ配信先バケットにログファイルが配信される。

aws s3 ls s3://<ログ配信先バケット名>/ --recursive

ログファイルを取得して中身を確認する。

aws s3 cp s3://<ログ配信先バケット名>/<ログファイルパス> /tmp/trail-log.json.gz
gunzip /tmp/trail-log.json.gz
cat /tmp/trail-log.json | jq '.Records[] | {eventName, errorCode, errorMessage}'
{
  "eventName": "GetObject",
  "errorCode": "AccessDenied",
  "errorMessage": "User: <プリンシパル ARN> is not authorized to perform: s3:GetObject on resource: \"arn:aws:s3:::<テスト用バケット名>/<オブジェクトキー>\" with an explicit deny in a resource control policy"
}

errorMessageexplicit deny in a resource control policy が記録されており、RCP による拒否であることがわかる。

注意事項

  • cloudtrail lookup-events は管理イベントと Insights イベントのみを検索でき、データイベントは検索対象外である。データイベントの拒否を確認するには、ログ配信先の S3 バケットからログファイルを直接取得する必要がある。
  • Control Tower が自動作成する Organization Trail(aws-controltower-BaselineCloudTrail)は管理イベントのみを記録する設定になっている。そのため、Organization Trail の S3 バケット(LogArchive アカウント)や CloudWatch Logs にもデータイベントは配信されない。データイベントを記録するには、上記のように別途証跡を作成する。Control Tower のランディングゾーン設定では Organization Trail をオプトアウトして自前の CloudTrail を管理することも可能であり、その場合は自前の Organization Trail でデータイベントを含めた設定を自由に行える。
同様の手順で SQS データイベントの証跡を作成し、ログファイルの配信・中身の確認まで実施した例は CT.SQS.PV.1 の補足 を参照。eventCategory: "Data", managementEvent: false として記録されることを確認済み。

Amazonアソシエイトリンク