上級 40分 Lesson 7

S3セキュリティ — アクセス制御・暗号化・Object Lock

S3のバケットポリシー評価、暗号化オプション、Object Lock、アクセスポイント、Macieとの連携を徹底解説

AWS S3 SCS-C03 暗号化 Security

概要

Amazon S3のセキュリティは多層的なアクセス制御と暗号化に支えられています。SCS-C03試験では単なる「ブロックパブリックアクセスを有効化する」というレベルではなく、ポリシー評価ロジック、暗号化オプション間のトレードオフ、Object Lockの制約、アクセスポイントの仕組みまでが問われます。

本講では、S3セキュリティの実装パターンと陥りやすい落とし穴をカバーします。


1. アクセス制御の多層構造

1.1 評価ロジック

S3へのアクセスが許可されるには、以下のすべてのステップを通過する必要があります:

S3アクセス制御の評価フロー

重要: 各層はANDで結合。すべてがAllow(またはNeutral)でなければアクセス拒否。

S3アクセス評価フロー

Loading diagram...


1.2 バケットポリシー vs ACL vs アクセスポイント

バケットポリシー

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyInsecureTransport",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    },
    {
      "Sid": "AllowGetObjectFromRole",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/MyRole"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/public/*"
    },
    {
      "Sid": "AllowPublicRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/download/*"
    }
  ]
}

特徴:

  • バケットレベルでの制御
  • Principal(誰)と Action(何)を明示的に指定
  • 明示的 Deny が最強(他のすべてのAllow/Grantを上書き)
  • リソースベースポリシー(クロスアカウントアクセスに必須)
  • サイズ上限 20KB(圧縮すると ~100 ステートメント程度)

サイズ超過対策:

# 複数のバケットに分散
# または AWS::S3::BucketPolicy を CloudFormation で管理して段階的に削除

# 最悪の場合、アクセスポイントポリシーに分散

ACL(廃止方向)

# ACLの確認
aws s3api get-bucket-acl --bucket my-bucket

# 出力例
{
  "Owner": {
    "DisplayName": "owner-name",
    "ID": "123456789..."
  },
  "Grants": [
    {
      "Grantee": {
        "Type": "CanonicalUser",
        "ID": "123456789..."
      },
      "Permission": "FULL_CONTROL"
    },
    {
      "Grantee": {
        "Type": "Group",
        "URI": "http://acs.amazonaws.com/groups/global/AllUsers"
      },
      "Permission": "READ"  # パブリック読み取り
    }
  ]
}

# オブジェクトレベル ACL の設定
aws s3api put-object --bucket my-bucket --key file.txt \
  --body file.txt --acl public-read

現在の推奨:

  • ACLは廃止予定
  • AWS はバケットポリシー+ ブロックパブリックアクセスへの移行を推奨
  • ただし、既存システムでは相互に確認されることもあり

アクセスポイント(Access Point)

# アクセスポイントの作成
aws s3control create-access-point \
  --account-id 123456789012 \
  --bucket my-bucket \
  --name my-ap

# 出力
{
  "AccessPointArn": "arn:aws:s3:us-east-1:123456789012:accesspoint/my-ap"
}

# アクセスポイントのポリシー設定
aws s3control put-access-point-policy \
  --account-id 123456789012 \
  --name my-ap \
  --policy file://policy.json

アクセスポイント専用ポリシー例:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/DataAnalyst"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:us-east-1:123456789012:accesspoint/my-ap/object/*",
        "arn:aws:s3:us-east-1:123456789012:accesspoint/my-ap"
      ]
    }
  ]
}

利点:

  • バケットポリシー 20KB 制限の回避(AP ごとに独立したポリシー)
  • VPC限定アクセス(VPC エンドポイント経由のみ)
  • マルチリージョンアクセスポイント(MRAP)で複数リージョン横断
  • Policy 評価ロジックが簡潔

1.3 Block Public Access(BPA)

BPA には 2つのレベル があります。

レベル1: アカウント全体

AWS CLI でアカウントレベル BPA を設定(BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, RestrictPublicBuckets をすべて true)

レベル2: バケット個別

AWS CLI でバケットレベル BPA を設定(BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, RestrictPublicBuckets をすべて true)

各フラグの意味:

フラグ効果使用場面
BlockPublicAclsPUT ACL public-* を拒否ACL経由のパブリック化を防止
IgnorePublicAcls既存の public ACL を無視(実装上 public ではない扱い)古い ACL 設定の遺物を無視
BlockPublicPolicys3:* Principal: "*" などのポリシーを拒否ポリシー経由のパブリック化を防止
RestrictPublicBucketsPublicRead/PublicListPermission を無視パブリックアクセスの完全遮断

階層的適用: バケットレベル BPA がアカウントレベルより強い場合、バケットレベルが優先。


2. 暗号化オプション

S3 の暗号化は 転送中(TLS)保存時(SSE) の2つに分かれます。

2.1 転送中の暗号化

# デフォルトで HTTPS(TLS 1.2以上)

# HTTP-only アクセスを明示的に拒否するポリシー
{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:*",
  "Resource": [
    "arn:aws:s3:::my-bucket",
    "arn:aws:s3:::my-bucket/*"
  ],
  "Condition": {
    "Bool": {
      "aws:SecureTransport": "false"
    }
  }
}

# CLI の署名バージョン確認
aws s3api head-bucket --bucket my-bucket
# -> Signature V4(デフォルト)

2.2 保存時の暗号化(SSE)

SSE-S3(AWS マネージドキー)

# CLI でオブジェクトをアップロード(SSE-S3)
aws s3 cp file.txt s3://my-bucket/file.txt \
  --sse AES256

# デフォルト暗号化を設定
aws s3api put-bucket-encryption \
  --bucket my-bucket \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "AES256"
        },
        "BucketKeyEnabled": false
      }
    ]
  }'

# 確認
aws s3api get-bucket-encryption --bucket my-bucket

特徴:

  • AWS が鍵を管理(透過的)
  • 追加コストなし
  • オブジェクトごとに異なる鍵でローテーション
  • キー削除不可(AWS 側で管理)

SSE-KMS(AWS KMS マネージドキー)

# KMS キーの作成
aws kms create-key \
  --description "S3 encryption key" \
  --origin AWS_KMS \
  --multi-region false

# キーの ARN を確認
aws kms describe-key --key-id arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012

# バケットにデフォルト暗号化を設定(KMS)
aws s3api put-bucket-encryption \
  --bucket my-bucket \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "aws:kms",
          "KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
        },
        "BucketKeyEnabled": true
      }
    ]
  }'

# オブジェクトをアップロード(KMS 指定)
aws s3 cp file.txt s3://my-bucket/file.txt \
  --sse aws:kms \
  --sse-kms-key-id arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012

特徴:

  • AWS KMS で鍵管理(キーのローテーション可能)
  • CloudTrail で暗号化/復号の監査可能
  • 追加コスト発生(KMS API 呼び出し)
  • IAM ポリシーで復号権限を制御可能

BucketKeyEnabled の意味:

BucketKeyEnabled: true:
  # S3 が KMS から一時的な "バケットキー" を取得
  # その後のオブジェクト暗号化はこのバケットキーで実施
  # → KMS API 呼び出し削減
  # → コスト削減(最大99%削減の可能性)
  # 注意: 復号時に実際のデータキー ID が見えなくなる場合がある

BucketKeyEnabled: false:
  # すべてのオブジェクトで KMS API 呼び出し
  # → 監査ログが詳細(何個のオブジェクトかわかる)
  # → コスト高い

SSE-C(カスタマープロバイデッドキー)

# クライアント側で暗号化キーを生成
openssl enc -aes-256-cbc -S $RANDOM -P -md sha256 -pass pass:"MySecretPassword"
# 出力:
# salt=XXXX
# key=YYYY...(この32バイトの16進数を使う)
# iv=ZZZZ...

# オブジェクトをアップロード(SSE-C)
aws s3 cp file.txt s3://my-bucket/file.txt \
  --sse-c AES256 \
  --sse-c-key "YYYY...(base64 エンコード済みの32バイト鍵)"

# ダウンロード時も同じ鍵を指定
aws s3 cp s3://my-bucket/file.txt file-decrypted.txt \
  --sse-c AES256 \
  --sse-c-key "YYYY...(base64 エンコード済みの32バイト鍵)"

# 注意: SSE-C は CLI では面倒なため、Boto3/SDK で使うことが多い

Python/Boto3 での SSE-C 例:

import boto3
import base64
import hashlib
import os

s3 = boto3.client('s3')

# 鍵の生成(256 ビット = 32 バイト)
encryption_key = os.urandom(32)
encryption_key_b64 = base64.b64encode(encryption_key).decode()
key_md5 = base64.b64encode(hashlib.md5(encryption_key).digest()).decode()

# アップロード
s3.put_object(
    Bucket='my-bucket',
    Key='file.txt',
    Body=b'My secret data',
    ServerSideEncryption='AES256',
    SSECustomerAlgorithm='AES256',
    SSECustomerKey=encryption_key_b64,
    SSECustomerKeyMD5=key_md5
)

# ダウンロード
response = s3.get_object(
    Bucket='my-bucket',
    Key='file.txt',
    SSECustomerAlgorithm='AES256',
    SSECustomerKey=encryption_key_b64,
    SSECustomerKeyMD5=key_md5
)
print(response['Body'].read())

特徴:

  • クライアントが鍵を管理
  • AWS は鍵を保存しない(リクエストに含まれるだけ)
  • CloudTrail では暗号化の事実だけが記録、鍵は記録されない
  • 追加コストなし
  • レプリケーション対象外(鍵が送信されないため)

SSE-S3 vs SSE-KMS vs SSE-C 比較表

特性SSE-S3SSE-KMSSSE-C
鍵管理AWS 完全管理AWS KMS(回転可能)クライアント管理
監査最小限詳細(CloudTrail)暗号化の事実のみ
コスト無料API 呼び出し時課金無料
キーローテーション自動(透過)手動可能クライアント側実装
レプリケーション可能可能(KMS 権限要)不可(鍵送信不可)
デフォルト暗号化可能可能不可(リクエスト指定)
ユースケース一般的コンプライアンス高度な制御

2.3 CSE(クライアント側暗号化)

# AWS Encryption SDK を使用
from aws_encryption_sdk import KMSMasterKeyProvider, encrypt, decrypt

kms_provider = KMSMasterKeyProvider(key_ids=[
    'arn:aws:kms:us-east-1:123456789012:key/12345678-...'
])

# 暗号化
plaintext = b'My secret data'
ciphertext, encryptor = encrypt(
    source=plaintext,
    key_provider=kms_provider
)

# S3 にアップロード(平文ではなく暗号文)
s3.put_object(
    Bucket='my-bucket',
    Key='file.txt',
    Body=ciphertext
)

# ダウンロード・復号
response = s3.get_object(Bucket='my-bucket', Key='file.txt')
encrypted_data = response['Body'].read()

plaintext, decryptor = decrypt(
    source=encrypted_data,
    key_provider=kms_provider
)
print(plaintext)

特徴:

  • S3 に到達する前に暗号化
  • AWS は平文を一度も見ない
  • コンプライアンス(HIPAA など)で必須
  • アプリケーション実装の負担大

3. S3 Object Lock

Object Lock は WORM(Write Once Read Many) を実現します。一度書き込まれたオブジェクトはロック解除まで削除・上書きできません

3.1 有効化と制約

# 新規バケット作成時に有効化(後付け不可)
aws s3api create-bucket \
  --bucket my-locked-bucket \
  --object-lock-enabled-for-bucket

# または CloudFormation で
# AWS::S3::Bucket:
#   ObjectLockEnabled: true
#   Versioning: Enabled  # 必須

# 既存バケットには Object Lock を後付けできない
# → 新しいバケット作成 → レプリケーション or 再アップロード

制約:

  1. バケット作成時のみ有効化可能(後付け不可)
  2. バージョニング必須(自動的に有効化)
  3. 削除禁止 on 既存オブジェクト(新規のみロック可能)
  4. ロック設定を 変更・削除できない(削除には管理者権限 + リーガルホールド解除が必要)

3.2 ガバナンスモード vs コンプライアンスモード

ガバナンスモード

# アップロード時にガバナンスモードで設定
aws s3api put-object \
  --bucket my-locked-bucket \
  --key important-file.txt \
  --body important-file.txt \
  --object-lock-mode GOVERNANCE \
  --object-lock-retain-until-date 2026-12-31T23:59:59Z

# 必要に応じて管理者が削除可能
aws s3api delete-object \
  --bucket my-locked-bucket \
  --key important-file.txt \
  --bypass-governance-retention  # 管理者権限必須

特徴:

  • IAM ロールで s3:BypassGovernanceRetention 権限がある場合、削除可能
  • デフォルト保持期間を設定可能
  • 削除前に --bypass-governance-retention フラグ必須

コンプライアンスモード

# アップロード時にコンプライアンスモードで設定
aws s3api put-object \
  --bucket my-locked-bucket \
  --key audit-log.txt \
  --body audit-log.txt \
  --object-lock-mode COMPLIANCE \
  --object-lock-retain-until-date 2026-12-31T23:59:59Z

# 削除試行(失敗)
aws s3api delete-object \
  --bucket my-locked-bucket \
  --key audit-log.txt
# → AccessDenied(IAM に何があっても削除不可)

# アップロード中に上書き試行(失敗)
aws s3api put-object \
  --bucket my-locked-bucket \
  --key audit-log.txt \
  --body new-content.txt
# → ObjectLockConflictException

特徴:

  • 絶対削除不可(IAM 権限・AWS 管理者でも削除不可)
  • RetainUntilDate に到達するまで削除できない
  • SEC/FINRA 規制対応に必須
  • 強力だが厳格

3.3 リーガルホールド

RetainUntilDate に関わらず、無期限にオブジェクトをロック。

# リーガルホールドを設定
aws s3api put-object-legal-hold \
  --bucket my-locked-bucket \
  --key case-file.txt \
  --legal-hold Status=ON

# 確認
aws s3api get-object-legal-hold \
  --bucket my-locked-bucket \
  --key case-file.txt
# → Status=ON

# リーガルホールド解除
aws s3api put-object-legal-hold \
  --bucket my-locked-bucket \
  --key case-file.txt \
  --legal-hold Status=OFF

# その後、RetainUntilDate が過ぎれば削除可能

ユースケース:

  • 訴訟関連のドキュメント
  • 調査中のログ
  • 予期しない保持期間延長

3.4 デフォルト保持期間

バケット全体で保持期間のデフォルトを設定:

# デフォルト保持期間を設定
aws s3api put-object-lock-configuration \
  --bucket my-locked-bucket \
  --object-lock-configuration '{
    "ObjectLockEnabled": "Enabled",
    "Rule": {
      "DefaultRetention": {
        "Mode": "COMPLIANCE",
        "Days": 365
      }
    }
  }'

# 確認
aws s3api get-object-lock-configuration --bucket my-locked-bucket

# オブジェクトアップロード(デフォルト期間が適用)
aws s3api put-object \
  --bucket my-locked-bucket \
  --key file.txt \
  --body file.txt
# → 自動的に365日間 COMPLIANCE モードでロック

4. バージョニングと MFA Delete

4.1 バージョニングの有効化

# バージョニングを有効化
aws s3api put-bucket-versioning \
  --bucket my-bucket \
  --versioning-configuration Status=Enabled

# 状態確認
aws s3api get-bucket-versioning --bucket my-bucket
# → Status=Enabled

# 同じキー名で複数アップロード
aws s3 cp file.txt s3://my-bucket/file.txt  # Version ID: abc123
aws s3 cp file.txt s3://my-bucket/file.txt  # Version ID: def456
aws s3 cp file.txt s3://my-bucket/file.txt  # Version ID: ghi789

# バージョン一覧表示
aws s3api list-object-versions --bucket my-bucket --prefix file.txt

4.2 MFA Delete の設定

MFA Delete により、バージョン削除に物理的な MFA デバイスが必須 になります。

# MFA Delete を有効化(root アカウントのアクセスキー + MFA が必須)
aws s3api put-bucket-versioning \
  --bucket my-bucket \
  --versioning-configuration Status=Enabled \
  --mfa "SERIAL-NUMBER TOTP-CODE"
# 例: --mfa "arn:aws:iam::123456789012:mfa/root-account-mfa 123456"

# MFA Delete が有効な状態を確認
aws s3api get-bucket-versioning --bucket my-bucket
# → MFADelete=Enabled

# バージョン削除(MFA が必須)
aws s3api delete-object \
  --bucket my-bucket \
  --key file.txt \
  --version-id abc123 \
  --mfa "SERIAL-NUMBER TOTP-CODE"

# MFA なしで削除試行(失敗)
aws s3api delete-object \
  --bucket my-bucket \
  --key file.txt \
  --version-id abc123
# → AccessDenied

制約:

  • IAM ユーザーでは MFA Delete を有効化できない(root アカウントのみ)
  • IAM ユーザーで削除する場合も MFA コード必須
  • --mfa パラメータは mfa/device-name serial-number code の形式

5. アクセスポイントの詳細

5.1 単一リージョンアクセスポイント

# VPC エンドポイント経由のみでアクセス可能
aws s3control create-access-point \
  --account-id 123456789012 \
  --bucket my-bucket \
  --name data-analyst-ap \
  --network-origin VPC \
  --vpc-configuration SubnetId=subnet-xxxxx,SecurityGroupId=sg-xxxxx

利点:

  • VPC 限定(インターネット経由のアクセス不可)
  • S3 VPC エンドポイント経由のみ
  • ログ記録が容易(アクセスポイント単位)

5.2 マルチリージョンアクセスポイント(MRAP)

# MRAP を作成
aws s3control create-multi-region-access-point \
  --account-id 123456789012 \
  --client-token unique-token \
  --details Regions=[{Bucket=my-bucket-us-east-1},{Bucket=my-bucket-eu-west-1}]

# 出力
{
  "RequestTokenArn": "arn:aws:s3::123456789012:async-request/mrap-abc123..."
}

# 作成の確認
aws s3control describe-multi-region-access-point \
  --account-id 123456789012 \
  --name my-mrap

# MRAP ARN で参照
# arn:aws:s3::[account-id]:accesspoint/*.[mrap-alias].mrap
# 例: arn:aws:s3::123456789012:accesspoint/*.mfzwi23gnjvgw.mrap

特徴:

  • 複数リージョンの複数バケットを1つのエンドポイント経由でアクセス
  • ジオロケーション自動ルーティング
  • グローバルアプリケーションのシンプルなアーキテクチャ

6. S3 Access Grants

Access Grants は IAM より細粒度で、S3 パス単位 のアクセス権限を付与します。

# Access Grants Location を登録
aws s3control create-access-grants-location \
  --account-id 123456789012 \
  --location-scope s3:///my-bucket/data/
  # → Location ID: 12345678-abcd-...

# Access Grant を発行
aws s3control create-access-grant \
  --account-id 123456789012 \
  --access-grants-location-id 12345678-abcd-... \
  --permission READ \
  --grantee Type=IAM,Identifier=arn:aws:iam::123456789012:user/alice

# 一時的な認証情報を取得
aws s3control get-data-access \
  --account-id 123456789012 \
  --target /data/  # Access Grants Location 内のパス

# 出力
{
  "Credentials": {
    "AccessKeyId": "...",
    "SecretAccessKey": "...",
    "SessionToken": "..."
  },
  "MatchedGrantTarget": "s3:///my-bucket/data/"
}

利点:

  • パス単位の権限管理(IAM よりシンプル)
  • 一時認証情報の自動生成
  • 外部パーティへのアクセス付与が容易

7. S3 Block Public Access(完全解説)

7.1 アカウントレベル vs バケットレベル

# アカウント全体で BPA を有効化
aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

# バケット個別で BPA を有効化(さらに厳格)
aws s3api put-public-access-block \
  --bucket my-bucket \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

# アカウント全体の BPA を確認
aws s3control get-public-access-block --account-id 123456789012

# バケット個別の BPA を確認
aws s3api get-public-access-block --bucket my-bucket

優先度:

  • バケットレベル BPA がアカウント BPA より強い場合、バケット設定が優先

7.2 試験で狙われるポイント

Q: Block Public Access がすべて有効でも、パブリックアクセスが発生する場合はあるか?

A: ACL が public-read でも、IgnorePublicAcls=true なら実質的に無視されます。 ただし ACL の設定は拒否されないため、古い ACL が残っている可能性あり。

Q: ポリシーで Principal=”*” でも BPA が防ぐのか?

A: BlockPublicPolicy=true の場合、Principal=”*” のポリシーの PUT が拒否される。 既に存在するポリシーは無視されません。


8. 暗号化のできないこと・制約

8.1 SSE-KMS の制約

制約1: KMS 鍵へのアクセス権が必須
  # 暗号化: s3:PutObject + kms:Decrypt 権限
  # 復号化: s3:GetObject + kms:Decrypt 権限
  # どちらかが Deny なら実行失敗

制約2: クロスリージョンレプリケーション(CRR)での暗号化変更制約
  # ソース: SSE-KMS(キー: us-east-1)
  # デスト: SSE-KMS(キー: eu-west-1)
  # レプリケーション時の自動鍵変換は不可
  # → 宛先バケットのデフォルト KMS キーが適用される
  # → ソースとデスト KMS キー ARN が異なる場合、別途設定必要

制約3: CloudFront 経由のアップロード(CRR で暗号化変換)
  # CloudFront → ソース S3(KMS 1)→ デスト S3(KMS 2)
  # → CloudFront は KMS 2 の復号権限が必須
  # → KMS 2 の キーポリシーに CloudFront のロール ARN 追加必要

8.2 SSE-C の制約

制約1: デフォルト暗号化としては使えない
  # デフォルト暗号化: SSE-S3 or SSE-KMS のみ
  # SSE-C はリクエストごとに鍵を指定する必要がある

制約2: レプリケーション対象外
  # ソース: SSE-C
  # デスト: S3 は暗号化されたコンテンツを受け取るが、
  #        鍵を保持していないためレプリケーション対象外
  # → 鍵が送信されないため

制約3: マルチパートアップロード時の鍵管理
  # 各パート: 同じ鍵で暗号化する必要がある
  # → 部分ごとに異なる鍵は使えない

制約4: CloudTrail で鍵が記録されない
  # s3:PutObject with SSE-C の成否は記録
  # → 使用した鍵の詳細は記録されない(セキュリティ上の理由)

8.3 Object Lock の制約

制約1: 後付け不可
  # 既存バケット → Object Lock 有効化 不可
  # → 新規バケット作成後、レプリケーション or 再アップロード必須

制約2: 削除禁止オブジェクトは他の属性変更も制限
  # ロック中のオブジェクト:
  #   - 削除: 不可
  #   - 上書き: 不可
  #   - メタデータ変更: 可能
  #   - タグ変更: 可能(バージョン単位)

制約3: バージョニングとの依存関係
  # Object Lock 有効 → 自動的に Versioning=Enabled
  # Versioning 無効化は不可(Object Lock がある限り)

9. 試験で狙われるポイント

ポイント1: ポリシー評価ロジック

シナリオ:
- IAM Role: s3:GetObject は Allow
- バケットポリシー: Principal="*" s3:GetObject は明示的 Deny
- Block Public Access: BlockPublicPolicy=true
- ACL: public-read

結果: Deny
理由: 明示的 Deny が最強。IAM Allow は上書きされる。
シナリオ2:
- IAM Role: s3:GetObject は Allow
- バケットポリシー: 何もない(Neutral)
- Block Public Access: すべて true(ただし IAM → バケットポリシーは評価されない)
- ACL: public-read

結果: Allow
理由: IAM Allow が有効。BPA は "*" Principal のポリシー/ACL のみをブロック。
     IAM Role 経由は評価されない。

ポイント2: SSE-KMS と IAM の組み合わせ

シナリオ:
- オブジェクト暗号化: SSE-KMS
- IAM ロール: s3:GetObject は Allow, kms:Decrypt は Deny
- KMS キーポリシー: このロールに Decrypt は許可

結果: GetObject 実行時 → KMS Decrypt 失敗 → AccessDenied
理由: IAM が全体で Deny すると、キーポリシーの Allow は上書きされない。
     どちらか一方でも Deny = 実行失敗。

ポイント3: MFA Delete の設定制限

Q: IAM ユーザー alice で MFA Delete を有効化できるか?
A: NO。MFA Delete はroot アカウントのみ設定可能。
   IAM ユーザーでは put-bucket-versioning --mfa は失敗。

ポイント4: Block Public Access の優先順位

シナリオ:
- アカウント BPA: すべて false
- バケット BPA: BlockPublicPolicy=true(他は false)

結果: BlockPublicPolicy のみ有効。バケット設定が優先。

10. ベストプラクティス

10.1 デフォルト設定

# すべてのバケットに推奨
1. Block Public Access を有効化(アカウント + バケット)
2. デフォルト暗号化を設定(SSE-S3 or SSE-KMS)
3. バージョニングを有効化
4. S3 Access Logging を有効化
5. CloudTrail S3 データイベントを記録

# CLI での一括設定
aws s3api put-public-access-block \
  --bucket my-bucket \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

aws s3api put-bucket-encryption \
  --bucket my-bucket \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "AES256"
        }
      }
    ]
  }'

aws s3api put-bucket-versioning \
  --bucket my-bucket \
  --versioning-configuration Status=Enabled

aws s3api put-bucket-logging \
  --bucket my-bucket \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "my-logging-bucket",
      "TargetPrefix": "s3-logs/"
    }
  }'

10.2 コンプライアンス対応

# FINRA / SEC 規制対応
# → Object Lock (COMPLIANCE) + MFA Delete

aws s3api create-bucket \
  --bucket my-compliance-bucket \
  --object-lock-enabled-for-bucket

aws s3api put-bucket-versioning \
  --bucket my-compliance-bucket \
  --versioning-configuration Status=Enabled \
  --mfa "arn:aws:iam::123456789012:mfa/root 123456"

aws s3api put-object-lock-configuration \
  --bucket my-compliance-bucket \
  --object-lock-configuration '{
    "ObjectLockEnabled": "Enabled",
    "Rule": {
      "DefaultRetention": {
        "Mode": "COMPLIANCE",
        "Years": 7
      }
    }
  }'

10.3 データレイク / 分析バケット

# 複数のアナリスト/チームがアクセス
# → アクセスポイント + IAM ロール

# アクセスポイント作成
aws s3control create-access-point \
  --account-id 123456789012 \
  --bucket my-data-lake \
  --name analytics-team-ap

# アクセスポイントポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::123456789012:role/DataAnalyst",
          "arn:aws:iam::123456789012:role/DataEngineer"
        ]
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:us-east-1:123456789012:accesspoint/analytics-team-ap/object/analytics/*",
        "arn:aws:s3:us-east-1:123456789012:accesspoint/analytics-team-ap"
      ]
    }
  ]
}

# IAM ロール側(信頼ポリシー)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

# IAM ロール側(実行ポリシー)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:us-east-1:123456789012:accesspoint/analytics-team-ap/object/analytics/*",
        "arn:aws:s3:us-east-1:123456789012:accesspoint/analytics-team-ap"
      ]
    }
  ]
}

11. 他サービスとの連携

11.1 CloudTrail と S3 Data Events

# CloudTrail で S3 オブジェクトレベルのアクセスを記録
aws cloudtrail put-event-selectors \
  --trail-name my-trail \
  --event-selectors '[
    {
      "ReadWriteType": "All",
      "IncludeManagementEvents": true,
      "DataResources": [
        {
          "Type": "AWS::S3::Object",
          "Values": [
            "arn:aws:s3:::my-bucket/*"
          ]
        }
      ]
    }
  ]'

# 結果: CloudTrail ログに以下が記録
# - s3:GetObject(読み取り)
# - s3:PutObject(書き込み)
# - s3:DeleteObject(削除)
# → CloudWatch Logs または CloudFormation へ送信可能

11.2 Amazon Macie(データ分類・検出)

# Macie を有効化
aws macie2 enable-macie

# S3 バケットで自動スキャンを有効化
aws macie2 enable-organization-admin-account \
  --admin-account-id 123456789012

# Macie がスキャン対象バケットを確認
aws macie2 list-managed-data-identifiers

# 機密データの検出結果を確認
aws macie2 get-findings-statistics \
  --finding-criteria '{
    "Criterion": {
      "type": {
        "Eq": ["Personal Identifiable Information"]
      }
    }
  }'

# 出力: PII(クレジットカード番号、SSN など)の検出位置

11.3 AWS Config(コンプライアンス監視)

# Config ルール: S3 バケットが BPA を有効化しているか
aws configservice put-config-rule --config-rule file://bpa-rule.json

# bpa-rule.json の内容
{
  "ConfigRuleName": "s3-block-public-access",
  "Description": "Checks that S3 Block Public Access is enabled",
  "Scope": {
    "ComplianceResourceTypes": [
      "AWS::S3::Bucket"
    ]
  },
  "Source": {
    "Owner": "AWS",
    "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
  }
}

# ルール評価結果
aws configservice describe-compliance-by-config-rule \
  --config-rule-names "s3-block-public-access"

# 出力: COMPLIANT / NON_COMPLIANT / INSUFFICIENT_DATA

11.4 CloudFront with S3 OAC(Origin Access Control)

# OAC の作成
aws cloudfront create-origin-access-control \
  --origin-access-control-config \
    Name=my-oac,OriginAccessControlOriginType=s3,SigningBehavior=always,SigningProtocol=sigv4

# バケットポリシー(CloudFront のみアクセス可能)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "StringEquals": {
          "aws:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E123ABC..."
        }
      }
    }
  ]
}

# 結果: CloudFront → S3 通信は暗号化+署名付き
#      ブラウザ → S3 直接アクセスは拒否

11.5 Lambda + S3 暗号化

# Lambda 実行ロール側のポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey"
      ],
      "Resource": "arn:aws:kms:us-east-1:123456789012:key/12345678-..."
    }
  ]
}

# Lambda コード例(Python)
import boto3
import json

s3_client = boto3.client('s3')

def lambda_handler(event, context):
    bucket = 'my-bucket'
    key = 'data.json'
    
    # SSE-KMS で暗号化されたオブジェクトを取得
    response = s3_client.get_object(Bucket=bucket, Key=key)
    
    # Lambda 実行ロールが kms:Decrypt 権限を持つ場合のみ復号
    data = json.loads(response['Body'].read().decode('utf-8'))
    
    return {
        'statusCode': 200,
        'body': json.dumps(data)
    }

11.6 Athena + S3 暗号化

# Athena クエリ結果を SSE-KMS で暗号化
aws athena start-query-execution \
  --query-string "SELECT * FROM my_table LIMIT 10" \
  --query-execution-context Database=default \
  --result-configuration \
    OutputLocation=s3://my-query-bucket/results/,\
    EncryptionConfiguration='{
      "EncryptionOption":"SSE_KMS",
      "KmsKey":"arn:aws:kms:us-east-1:123456789012:key/12345678-..."
    }'

# Athena のテーブル ロケーション(SSE-KMS)
# CREATE EXTERNAL TABLE my_table (
#   id INT,
#   name STRING
# )
# STORED AS PARQUET
# LOCATION 's3://my-data-bucket/parquet/'
# TBLPROPERTIES (
#   'classification' = 'parquet',
#   'encryption' = 'kms'
# )

12. できないこと・制約(再整理)

S3 全般

制約理由回避方法
バケットポリシー 20KB 上限5000 ステートメント上限アクセスポイント/条件圧縮
Object Lock 後付けバージョニング必須だが、既存バケット無効化不可新規バケット作成
SSE-C レプリケーション不可鍵が送信されないソース側で復号 or SSE-KMS に変更
ACL の将来廃止セキュリティ向上のためポリシー/AP に移行
MFA Delete(IAM ユーザー設定不可)AWS セキュリティ仕様root アカウント使用

13. まとめ

セキュリティ4層防御

  1. アイデンティティ層: IAM ロール/ユーザーポリシー
  2. リソース層: バケットポリシー + アクセスポイントポリシー
  3. アクセス制御層: Block Public Access
  4. 暗号化層: SSE-S3 / SSE-KMS / SSE-C + CloudTrail 監査

試験出題傾向

  • ポリシー評価ロジック の理解(AND/OR、Deny の最強性)
  • 暗号化オプション間のトレードオフ (コスト vs 監査可能性)
  • Object Lock の制約 (後付け不可、COMPLIANCE の絶対性)
  • MFA Delete の設定制限 (root のみ)
  • アクセスポイントの実装パターン (VPC限定、MRAP)

14. 連携パターン実装例

ログ集約バケット(コンプライアンス準備)

#!/bin/bash

# 1. ログ集約バケット作成(Object Lock + MFA Delete)
aws s3api create-bucket \
  --bucket audit-logs-bucket \
  --object-lock-enabled-for-bucket \
  --region us-east-1

# 2. BPA を有効化
aws s3api put-public-access-block \
  --bucket audit-logs-bucket \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

# 3. バージョニング有効化
aws s3api put-bucket-versioning \
  --bucket audit-logs-bucket \
  --versioning-configuration Status=Enabled

# 4. デフォルト保持期間(7年 COMPLIANCE)
aws s3api put-object-lock-configuration \
  --bucket audit-logs-bucket \
  --object-lock-configuration '{
    "ObjectLockEnabled": "Enabled",
    "Rule": {
      "DefaultRetention": {
        "Mode": "COMPLIANCE",
        "Years": 7
      }
    }
  }'

# 5. SSE-KMS デフォルト暗号化
aws s3api put-bucket-encryption \
  --bucket audit-logs-bucket \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "aws:kms",
          "KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/..."
        },
        "BucketKeyEnabled": true
      }
    ]
  }'

# 6. ログ記録
aws s3api put-bucket-logging \
  --bucket audit-logs-bucket \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "audit-logs-bucket",
      "TargetPrefix": "s3-access-logs/"
    }
  }'

# 7. CloudTrail データイベント記録
aws cloudtrail put-event-selectors \
  --trail-name compliance-trail \
  --event-selectors '[
    {
      "ReadWriteType": "All",
      "IncludeManagementEvents": true,
      "DataResources": [
        {
          "Type": "AWS::S3::Object",
          "Values": ["arn:aws:s3:::audit-logs-bucket/*"]
        }
      ]
    }
  ]'

echo "Compliance audit bucket setup complete"

このスクリプトにより以下が実現:

  • 無期限削除不可(Object Lock COMPLIANCE)
  • 改ざん防止(バージョニング + MFA Delete)
  • 監査可能(CloudTrail + S3 アクセスログ)
  • 暗号化(SSE-KMS + バケットキー最適化)

まとめ表

トピック重点ポイント
評価ロジックDeny が最強、AND で結合、複数層で評価
バケットポリシー20KB 制限、クロスアカウント必須
暗号化SSE-S3 無料/透明、SSE-KMS 監査可能、SSE-C クライアント完全制御
Object Lock後付け不可、COMPLIANCE は絶対削除不可
MFA Deleteroot のみ設定可能
BPA4つのフラグ、層状の適用優先度
APバケットポリシーの 20KB 回避、VPC限定、MRAP グローバル
監査CloudTrail + Macie + Config で多角的監視