駆逐してやる！！AWS Security Hubの指摘を…この世から…一匹残らず！！ ～ S3編 (CLI) ～

# Security Hub の指摘、駆逐してますか？

<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/50a8935b-a8b2-6277-a367-dd5d909ecb42.png">

* **とりあえず、Security Hub を有効化したけど、セキュリティスコアが低くて困っている…**
* **Security Hub の検出結果 (Findings) にどう対処すればよいのかわからない…**
* **S3 のセキュリティを強化したい…**

そんな方々のために、本記事では Security Hub の S3 に関する指摘事項を一匹残らず駆逐する方法を解説します。

また、解説と合わせて、Security Hub に指摘されない S3 バケットを作成するための CLI スクリプトも紹介していきます。
「解説はいいから早よ CLI をよこせ」という方はこちらをどうぞ。
　↓　↓　↓
[**CLI を捧げよ！**](#cli-を捧げよ)

:::note info
本記事は [駆逐してやる！！AWS Security Hubの指摘を…この世から…一匹残らず！！ ～ S3編 (CloudFormation) ～](https://qiita.com/ozzy3/items/66ef9e004f44d4cd9bfa) の CLI 版です。CLI 以外の内容（解説等）は同じです。
:::

## CLI のパラメータ指定方法について

本記事では CLI のパラメータ指定方法として **--cli-input-yaml** オプションを使用しています。

コマンドによってはパラメータの数が多く、それらをすべてコマンドのオプションで指定するのは大変なので、パラメータを YAML 形式で定義できる -cli-input-yaml オプションを使用しています。（まぁ、好みです）

詳しくは以下を参照してください。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-usage-skeleton.html

https://dev.classmethod.jp/articles/using-aws-cli-cli-inpute-parameter-using-heredocument/


# AWS Security Hub とは

一言でいうと、複数のAWSサービスのセキュリティアラートを集約して一元管理するためのサービスです。

https://aws.amazon.com/jp/security-hub/

## セキュリティ標準 (Standards)

Security Hub は、**AWS のベストプラクティスや業界標準に基づいたセキュリティチェック機能** を備えています。
投稿時点 (2022/03/21) では、以下の **セキュリティ標準 (Standards)** が用意されています。

* AWS 基礎セキュリティのベストプラクティス (AWS Foundational Security Best Practices)
* CIS AWS Foundations Benchmark
* Payment Card Industry Data Security Standard (PCI DSS)


## コントロール (Controls)

セキュリティ標準に含まれる個々のチェック項目を **コントロール (Controls)** と呼びます。
例えば、AWS Foundational Security Best Practices には以下のようなコントロール (Controls) が含まれます。

* AWS Foundational Security Best Practices
  * [S3.1] S3 ブロックパブリックアクセス設定を有効にする必要があります
  * [S3.2] S3 バケットではパブリック読み取りアクセスを禁止する必要があります
  ・・・

## 検出結果 (Findings)

セキュリティチェックにより検出された結果を **検出結果 (Findings)** と呼びます。
ただし、本記事ではわかりやすくするために **指摘** と表現します。


# 駆逐してやる！！

ここからは指摘の駆逐方法を解説していきます。

:::note warn
既存バケットの設定を変更する場合、変更による影響を十分に検証してください。
投稿内容を利用したことにより生じた損害について、著者は一切の責任を負いません。
:::


## AWS Foundational Security Best Practices

AWS Foundational Security Best Practices は名前のとおり、AWS における基礎的なベストプラクティスに基づいたセキュリティ標準です。

<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/0e5510e1-9a2a-8c54-4623-3929abc50f05.png">

https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp.html

---


### [S3.1] S3 ブロックパブリックアクセス設定を有効にする必要があります

[[S3.1] S3 Block Public Access setting should be enabled](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-1)
重大度：中

この指摘を駆逐するには、アカウントレベルのブロックパブリックアクセス設定を有効にします。

ブロックパブリックアクセス設定には、アカウントレベルとバケットレベルの2種類の設定が存在します。
[S3.1] はアカウントレベルの設定です。バケットレベルの設定は [S3.8] が該当します。

アカウントレベルの設定は、マネジメントコンソール、または、CLI で設定します。
CloudFormation では設定できません。


#### マネジメントコンソール

以下の画面から設定します。

Amazon S3 > このアカウントのブロックパブリックアクセス設定

<img width="800" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/744b541c-489a-b644-996a-de1c7dbfbc4a.png">


#### CLI

以下のコマンドで設定します。
[s3control put-public-access-block](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-public-access-block.html)

```shell
#===========================================================
# アカウントレベルのブロックパブリックアクセス設定
#===========================================================
INPUT_YAML=$(cat << EOS
AccountId: "${AWS_ACCOUNT_ID}"
PublicAccessBlockConfiguration:
  BlockPublicAcls: true
  IgnorePublicAcls: true
  BlockPublicPolicy: true
  RestrictPublicBuckets: true
EOS
)

aws s3control put-public-access-block --cli-input-yaml "$INPUT_YAML"
```

* **${AWS_ACCOUNT_ID}** には、対象のAWSアカウントID（数字12桁）を指定してください。


#### 参考

https://docs.aws.amazon.com/AmazonS3/latest/userguide/configuring-block-public-access-account.html

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3control/put-public-access-block.html

---

### [S3.2] S3 バケットではパブリック読み取りアクセスを禁止する必要があります

[[S3.2] S3 buckets should prohibit public read access](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-2)
重大度：重大

この指摘の駆逐方法は [S3.8] に含まれます。
以下を参照してください。

* AWS Foundational Security Best Practices
  [[S3.8] S3 ブロックパブリックアクセス設定は、バケットレベルで有効にする必要があります](#s38-s3-ブロックパブリックアクセス設定はバケットレベルで有効にする必要があります)

---

### [S3.3] S3 バケットはパブリック書き込みアクセスを禁止する必要があります

[[S3.3] S3 buckets should prohibit public write access](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-3)
重大度：重大

この指摘の駆逐方法は [S3.8] に含まれます。
以下を参照してください。

* AWS Foundational Security Best Practices
  [[S3.8] S3 ブロックパブリックアクセス設定は、バケットレベルで有効にする必要があります](#s38-s3-ブロックパブリックアクセス設定はバケットレベルで有効にする必要があります)

---

### [S3.4] S3 バケットでは、サーバー側の暗号化を有効にする必要があります

[[S3.4] S3 buckets should have server-side encryption enabled](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-4)
重大度：中

この指摘を駆逐するには、S3 バケットのサーバー側の暗号化を有効にします。

デフォルトのサーバー側の暗号化は以下の2種類から選択できます。

* S3 で管理されたキーを使用する **SSE-S3**
* KMS に保存されたキーを使用する **SSE-KMS**


#### CLI

[**s3api put-bucket-encryption**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-encryption.html) コマンドで設定します。

```shell:SSE-S3
#===========================================================
# サーバー側の暗号化設定 (SSE-S3)
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
ServerSideEncryptionConfiguration:
  Rules:
    - ApplyServerSideEncryptionByDefault:
        SSEAlgorithm: AES256
EOS
)

aws s3api put-bucket-encryption --cli-input-yaml "$INPUT_YAML"
```

```shell:SSE-KMS
#===========================================================
# サーバー側の暗号化設定 (SSE-KMS)
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
ServerSideEncryptionConfiguration:
  Rules:
    - ApplyServerSideEncryptionByDefault:
        SSEAlgorithm: aws:kms
        KMSMasterKeyID: ${KMS_KEY_ID}
      BucketKeyEnabled: true
EOS
)

aws s3api put-bucket-encryption --cli-input-yaml "$INPUT_YAML"
```

* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。
* **${KMS_KEY_ID}** はパラメータです。暗号化に使用する KMS Key ID を指定します。
* **BucketKeyEnabled** はオプションですが、SSE-KMS の場合は有効化することをおすすめします。バケットキーを有効化することにより、S3 から KMS へのリクエストを減らし、コストを削減することができます。

#### 参考

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/bucket-encryption.html

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/bucket-key.html

---


### [S3.5] S3 バケットは、Secure Socket Layer を使用するためのリクエストが必要です

[[S3.5] S3 buckets should require requests to use Secure Socket Layer](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-5)
重大度：中

この指摘を駆逐するには、S3 バケットに対する操作が HTTPS リクエストのみを受け入れる（HTTP リクエストは拒否する）ように設定します。

#### CLI

[**s3api put-bucket-policy**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-policy.html) コマンドを用いて、バケットポリシーに以下のステートメントを追加します。
ポイントは **aws:SecureTransport** です。
aws:SecureTransport が false の場合＝ HTTPS リクエストではない場合、S3 バケットおよびオブジェクトに対するすべての操作を拒否します。

:::note warn
put-bucket-policy コマンドでバケットポリシーを更新する場合、既存のバケットポリシーを上書きしてしまわないようご注意ください。既存のバケットポリシーがある場合、POLICY_JSON は既存のバケットポリシーに DenyInsecureRequests ステートメントを追加する形としてください。
:::

```shell
#===========================================================
# HTTP拒否ポリシー
#===========================================================
POLICY_JSON=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyInsecureRequests",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}",
                "arn:aws:s3:::${BUCKET_NAME}/*"
            ],
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": false
                }
            }
        }
    ]
}
EOS
)

INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
Policy: '${POLICY_JSON}'
EOS
)

aws s3api put-bucket-policy --cli-input-yaml "$INPUT_YAML"
```

* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。

#### 参考

https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-bucket-policy-for-config-rule/

---


### [S3.6] 他のユーザーに付与された Amazon S3 のアクセス許可AWSバケットポリシーのアカウントは制限する必要があります

[[S3.6] Amazon S3 permissions granted to other AWS accounts in bucket policies should be restricted](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-6)
重大度：高

この指摘は、他アカウントのユーザーやロールに対して、以下のいずれかの操作を明示的に許可している場合に指摘されます。

* s3:DeleteBucketPolicy
* s3:PutBucketAcl
* s3:PutBucketPolicy
* s3:PutEncryptionConfiguration
* s3:PutObjectAcl

つまり、他アカウントのユーザーやロールに対して、これらの操作を明示的に許可していなければ、指摘されることはありません。

なお、他アカウントのユーザーやロールに対する **明示的な拒否** を設定しておくと、意図せずに許可してしまうことを防ぐことができます。

#### CLI

[**s3api put-bucket-policy**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-policy.html) コマンドを用いて、バケットポリシーに以下のステートメントを追加します。
ポイントは **NotPrincipal** です。
NotPrincipal に自アカウントIDを指定することにより、自アカウント以外＝他アカウントに対する明示的な拒否を設定しています。

:::note warn
put-bucket-policy コマンドでバケットポリシーを更新する場合、既存のバケットポリシーを上書きしてしまわないようご注意ください。既存のバケットポリシーがある場合、POLICY_JSON は既存のバケットポリシーに DenyInsecureRequests ステートメントを追加する形としてください。
:::

```shell
#===========================================================
# 他アカウントからの機密API操作拒否ポリシー
#===========================================================
POLICY_JSON=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenySensitiveActionsFromOtherAccounts",
            "Effect": "Deny",
            "NotPrincipal": {
                "AWS": "${AWS_ACCOUNT_ID}"
            },
            "Action": [
                "s3:DeleteBucketPolicy",
                "s3:PutBucketAcl",
                "s3:PutBucketPolicy",
                "s3:PutEncryptionConfiguration",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}",
                "arn:aws:s3:::${BUCKET_NAME}/*"
            ]
        }
    ]
}
EOS
)

INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
Policy: '${POLICY_JSON}'
EOS
)

aws s3api put-bucket-policy --cli-input-yaml "$INPUT_YAML"
```

* **${AWS_ACCOUNT_ID}** には、対象のAWSアカウントID（数字12桁）を指定してください。
* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。

#### 参考

https://aws.amazon.com/jp/premiumsupport/knowledge-center/explicit-deny-principal-elements-s3/

---


### [S3.7] （欠番）

[S3.7] は存在しません。欠番のようです。

---


### [S3.8] S3 ブロックパブリックアクセス設定は、バケットレベルで有効にする必要があります

[[S3.8] S3 Block Public Access setting should be enabled at the bucket level](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-8)
重大度：高

この指摘を駆逐するには、バケットレベルのブロックパブリックアクセス設定を有効にします。

「[[S3.1] S3 ブロックパブリックアクセス設定を有効にする必要があります](#s31-s3-ブロックパブリックアクセス設定を有効にする必要があります)」は、アカウントレベルの設定でしたが、こちらはバケットレベルの設定です。
アカウントレベルで設定していても、Security Hub はバケットレベルの設定も要求してきます。

#### CLI

[**s3api put-public-access-block**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-public-access-block.html) コマンドで設定します。

```shell
#===========================================================
# バケットレベルのブロックパブリックアクセス設定
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
PublicAccessBlockConfiguration:
  BlockPublicAcls: true
  IgnorePublicAcls: true
  BlockPublicPolicy: true
  RestrictPublicBuckets: true
EOS
)

aws s3api put-public-access-block --cli-input-yaml "$INPUT_YAML"
```

#### 参考

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/access-control-block-public-access.html

---


### [S3.9] S3 バケットサーバーアクセスログを有効にする必要があります

[[S3.9] S3 bucket server access logging should be enabled](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#fsbp-s3-9)
重大度：中

この指摘を駆逐するには、S3 バケットに対するアクセスログを有効にします。

**ただし、アクセスログ保存用バケット自体のロギング設定は留意する必要があります。**
以下のとおり、ソースバケット（ロギング対象）とターゲットバケット（ログ保存先）が同じ場合、いくつかの留意事項があるため、アクセスログ保存用バケット自体のロギングは設定しない判断が好ましいケースもあります。
この場合、アクセスログ保存用バケットに対する [S3.9] の指摘は抑制するのがよいでしょう。

> ソースバケットとターゲットバケットが同じである場合、バケットに書き込まれるログに関する追加のログが作成されます。これは、ストレージの請求額がいくらか増える可能性があるため、望ましくない場合があります。また、ログに関する追加のログのために、必要なログを見つけにくくなります。

引用：https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/ServerLogs.html



#### CLI

[**s3api put-bucket-logging**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-logging.html#put-bucket-logging) コマンドで設定します。

```shell
#===========================================================
# ロギング設定
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
BucketLoggingStatus:
  LoggingEnabled:
    TargetBucket: ${LOGGING_BUCKET_NAME}
    TargetPrefix: ${BUCKET_NAME}
EOS
)

aws s3api put-bucket-logging --cli-input-yaml "$INPUT_YAML"
```

* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。
* **${LOGGING_BUCKET_NAME}** はパラメータです。ログ保存先のバケット名を指定します。
* **TargetPrefix** はオプションですが、複数バケットのアクセスログをひとつのバケットに集約する際に、どのバケットのアクセスログかを判別できるよう、バケット名をプレフィックスとして設定することをおすすめします。

#### 参考

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/ServerLogs.html

---


## CIS AWS Foundations Benchmark

CIS AWS Foundations Benchmark は CIS (Center of Internet Security) のベンチマークに基づいたセキュリティ標準です。

<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/af2561c0-2367-bfbc-cfe1-0be7104c1a81.png">

https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-cis.html

https://www.cisecurity.org/benchmark/amazon_web_services


---


### 2.3 — CloudTrail ログが記録する S3 バケットがパブリックにアクセスできないことを確認します

[2.3 – Ensure the S3 bucket CloudTrail logs to is not publicly accessible](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-2.3)
重大度：重大

この指摘を駆逐するには、CloudTrail ログバケットに対して、ブロックパブリックアクセスを設定します。

ブロックパブリックアクセスの設定方法は、以下を参照してください。

* AWS Foundational Security Best Practices
  [[S3.8] S3 ブロックパブリックアクセス設定は、バケットレベルで有効にする必要があります](#s38-s3-ブロックパブリックアクセス設定はバケットレベルで有効にする必要があります)

---


### 2.6 — CloudTrail S3 バケットアクセスログ記録が CloudTrail S3 バケットで有効になっていることを確認します

[2.6 – Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-2.6)
重大度：低

この指摘を駆逐するには、CloudTrail ログバケットに対して、アクセスログ記録を設定します。

アクセスログ記録の設定方法は、以下を参照してください。

* AWS Foundational Security Best Practices
  [[S3.9] S3 バケットサーバーアクセスログを有効にする必要があります](#s39-s3-バケットサーバーアクセスログを有効にする必要があります)

---


## Payment Card Industry Data Security Standard (PCI DSS)

Payment Card Industry Data Security Standard (PCI DSS) は、クレジットカード業界のセキュリティ標準です。

<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/04b035d8-6145-ee3b-a9f5-2861cb539e2d.png">

<!--
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/ee58afc4-bac1-a913-bf55-97df5ac8d9f8.png">
-->

https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-pcidss.html

https://www.jcdsc.org/pci_dss.php

---


### [PCI.S3.1] S3 バケットはパブリック書き込みアクセスを禁止する必要があります

[[PCI.S3.1] S3 buckets should prohibit public write access](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-pci-controls.html#pcidss-s3-1)
重大度：重大

以下の指摘と同様です。

* AWS Foundational Security Best Practices
  [[S3.3] S3 バケットはパブリック書き込みアクセスを禁止する必要があります](#s33-s3-バケットはパブリック書き込みアクセスを禁止する必要があります)
    * 駆逐方法
      [[S3.8] S3 ブロックパブリックアクセス設定は、バケットレベルで有効にする必要があります](#s38-s3-ブロックパブリックアクセス設定はバケットレベルで有効にする必要があります)

---


### [PCI.S3.2] S3 バケットではパブリック読み取りアクセスを禁止する必要があります

[[PCI.S3.2] S3 buckets should prohibit public read access](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-pci-controls.html#pcidss-s3-2)
重大度：重大

以下の指摘と同様です。

* AWS Foundational Security Best Practices
  [[S3.2] S3 バケットではパブリック読み取りアクセスを禁止する必要があります](#s32-s3-バケットではパブリック読み取りアクセスを禁止する必要があります)
    * 駆逐方法
      [[S3.8] S3 ブロックパブリックアクセス設定は、バケットレベルで有効にする必要があります](#s38-s3-ブロックパブリックアクセス設定はバケットレベルで有効にする必要があります)

---


### [PCI.S3.3] S3 バケットでクロスリージョンレプリケーションを有効にする必要があります

[[PCI.S3.3] S3 buckets should have cross-region replication enabled](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-pci-controls.html#pcidss-s3-3)
重大度：低

この指摘を駆逐するには、S3 バケットのクロスリージョンレプリケーションを有効にします。

なお、レプリケーションを有効にするには、予めレプリケート元とレプリケート先の両方のバケットで、バージョニングを有効にしておく必要があります。

#### CLI

クロスリージョンレプリケーションを設定するにあたり、レプリケーション用の IAM Role が必要になります。
クロスリージョンレプリケーションの設定は [**s3api put-bucket-replication**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-replication.html) コマンドで設定します。

```shell
#===========================================================
# IAMポリシー作成
#===========================================================
POLICY_JSON=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowSourceBucketAccess",
            "Effect": "Allow",
            "Action": [
                "s3:GetReplicationConfiguration",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}"
            ]
        },
        {
            "Sid": "AllowSourceObjectAccess",
            "Effect": "Allow",
            "Action": [
                "s3:GetObjectVersionForReplication",
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionTagging"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}/*"
            ]
        },
        {
            "Sid": "AllowDestinationObjectAccess",
            "Effect": "Allow",
            "Action": [
                "s3:ReplicateObject",
                "s3:ReplicateDelete",
                "s3:ReplicateTags"
            ],
            "Resource": [
                "arn:aws:s3:::${REPLICATION_BUCKET_NAME}/*"
            ]
        }
    ]
}
EOS
)

INPUT_YAML=$(cat << EOS
PolicyName: ${POLICY_NAME}
PolicyDocument: '${POLICY_JSON}'
EOS
)

aws iam create-policy --cli-input-yaml "$INPUT_YAML"

#===========================================================
# IAMロール作成
#===========================================================
POLICY_JSON=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "s3.amazonaws.com"
                ]
            },
            "Action": [
                "sts:AssumeRole"
            ]
        }
    ]
}
EOS
)

INPUT_YAML=$(cat << EOS
RoleName: ${ROLE_NAME}
AssumeRolePolicyDocument: '${POLICY_JSON}'
EOS
)

aws iam create-role --cli-input-yaml "$INPUT_YAML"

#===========================================================
# IAMポリシーのアタッチ
#===========================================================
INPUT_YAML=$(cat << EOS
RoleName: ${ROLE_NAME}
PolicyArn: arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${POLICY_NAME}
EOS
)

aws iam attach-role-policy --cli-input-yaml "$INPUT_YAML"

#===========================================================
# バージョニング有効化
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
VersioningConfiguration:
  Status: Enabled
EOS
)

aws s3api put-bucket-versioning --cli-input-yaml "$INPUT_YAML"

#===========================================================
# レプリケーション有効化
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
ReplicationConfiguration:
  Role: arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME}
  Rules:
    - Status: Enabled
      Priority: 1
      Filter:
        Prefix: ""
      DeleteMarkerReplication:
        Status: Disabled
      Destination:
        Bucket: arn:aws:s3:::${REPLICATION_BUCKET_NAME}
EOS
)

aws s3api put-bucket-replication --cli-input-yaml "$INPUT_YAML"
```

* **${AWS_ACCOUNT_ID}** には、対象のAWSアカウントID（数字12桁）を指定してください。
* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。
* **${REPLICATION_BUCKET_NAME}** はパラメータです。レプリケーション先のバケット名を指定します。
* **${POLICY_NAME}** はパラメータです。作成するIAMポリシー名を指定します。
* **${ROLE_NAME}** はパラメータです。作成するIAMロール名を指定します。

:::note warn
ここではクロスリージョンレプリケーションを実現する上で最低限の設定を掲載しています。[s3api put-bucket-replication](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-replication.html) コマンドの設定は多岐に渡りますので、ユースケースに応じて適宜設定を見直してください。
:::

#### 参考

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/replication.html

---


### [PCI.S3.4] S3 バケットでは、サーバー側の暗号化を有効にする必要があります

[[PCI.S3.4] S3 buckets should have server-side encryption enabled](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-pci-controls.html#pcidss-s3-4)
重大度：中

以下の指摘と同様です。

* AWS Foundational Security Best Practices
  [[S3.4] S3 バケットでは、サーバー側の暗号化を有効にする必要があります](#s34-s3-バケットではサーバー側の暗号化を有効にする必要があります)

---


### [PCI.S3.5] S3 バケットは、Secure Socket Layer を使用するリクエストを要求する必要があります

[[PCI.S3.5] S3 buckets should require requests to use Secure Socket Layer](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-pci-controls.html#pcidss-s3-5)
重大度：中

以下の指摘と同様です。

* AWS Foundational Security Best Practices
  [[S3.5] S3 バケットは、Secure Socket Layer を使用するためのリクエストが必要です](#s35-s3-バケットはsecure-socket-layer-を使用するためのリクエストが必要です)

---


### [PCI.S3.6] S3 ブロックパブリックアクセス設定を有効にする必要があります

[[PCI.S3.6] S3 Block Public Access setting should be enabled](https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-pci-controls.html#pcidss-s3-6)
重大度：中

以下の指摘と同様です。

* AWS Foundational Security Best Practices
  [[S3.1] S3 ブロックパブリックアクセス設定を有効にする必要があります](#s31-s3-ブロックパブリックアクセス設定を有効にする必要があります)

---


# Security Hub 指摘以外でやるべき設定

ここからは Security Hub では指摘されませんが、やっておいたほうがよい推奨する設定を紹介します。

---


## ACL無効化

ACL (Access Control List) の無効化は AWS re:Invent 2021 で発表された機能です。
AWS も無効化を推奨しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/47b688cd-c8aa-8d0d-33ff-cf5d7c373e1d.png)

### CLI

[**s3api put-bucket-ownership-controls**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-ownership-controls.html) コマンドで設定します。
ObjectOwnership の値に **BucketOwnerEnforced** を設定することにより、ACL が無効化されます。

```shell
#===========================================================
# ACL無効化設定
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
OwnershipControls:
  Rules:
    - ObjectOwnership: BucketOwnerEnforced
EOS
)

aws s3api put-bucket-ownership-controls --cli-input-yaml "$INPUT_YAML"
```

* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。

### 参考

https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-s3-object-ownership-simplify-access-management-data-s3/

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/about-object-ownership.html

---


## バージョニング

S3 バケットのバージョニングを有効にすると、オブジェクトを誤って削除したり上書きしてしまっても、復元することができます。
ただし、複数のバージョンを保持する分、ストレージコストは増加するので、その点は理解した上で使用しましょう。

### CLI

[**s3api put-bucket-versioning**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-versioning.html) コマンドで設定します。

```shell
#===========================================================
# バージョニング設定
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
VersioningConfiguration:
  Status: Enabled
EOS
)

aws s3api put-bucket-versioning --cli-input-yaml "$INPUT_YAML"
```

* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。

### 参考

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/Versioning.html

---


## S3 のセキュリティベストプラクティス

ベストプラクティスは読んでおきましょう。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/security-best-practices.html

---


# CLI を捧げよ！

ここまで解説してきた内容をすべて含めた CLI スクリプトがこちらです。
必ずしも、すべての設定が必要とは限らないので、ユースケースに応じて適宜見直してください。

## アカウントレベルのブロックパブリックアクセス設定

```shell:secure_s3_account.sh
#===========================================================
# アカウントレベルのブロックパブリックアクセス設定
#===========================================================
INPUT_YAML=$(cat << EOS
AccountId: "${AWS_ACCOUNT_ID}"
PublicAccessBlockConfiguration:
  BlockPublicAcls: true
  IgnorePublicAcls: true
  BlockPublicPolicy: true
  RestrictPublicBuckets: true
EOS
)

aws s3control put-public-access-block --cli-input-yaml "$INPUT_YAML"
```

* **${AWS_ACCOUNT_ID}** には、対象のAWSアカウントID（数字12桁）を指定してください。


## レプリケーション用IAMロールの作成

```shell:s3_replication_role.sh
#===========================================================
# IAMポリシー作成
#===========================================================
POLICY_JSON=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowSourceBucketAccess",
            "Effect": "Allow",
            "Action": [
                "s3:GetReplicationConfiguration",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}"
            ]
        },
        {
            "Sid": "AllowSourceObjectAccess",
            "Effect": "Allow",
            "Action": [
                "s3:GetObjectVersionForReplication",
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionTagging"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}/*"
            ]
        },
        {
            "Sid": "AllowDestinationObjectAccess",
            "Effect": "Allow",
            "Action": [
                "s3:ReplicateObject",
                "s3:ReplicateDelete",
                "s3:ReplicateTags"
            ],
            "Resource": [
                "arn:aws:s3:::${REPLICATION_BUCKET_NAME}/*"
            ]
        }
    ]
}
EOS
)

INPUT_YAML=$(cat << EOS
PolicyName: ${POLICY_NAME}
PolicyDocument: '${POLICY_JSON}'
EOS
)

aws iam create-policy --cli-input-yaml "$INPUT_YAML"

#===========================================================
# IAMロール作成
#===========================================================
POLICY_JSON=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "s3.amazonaws.com"
                ]
            },
            "Action": [
                "sts:AssumeRole"
            ]
        }
    ]
}
EOS
)

INPUT_YAML=$(cat << EOS
RoleName: ${ROLE_NAME}
AssumeRolePolicyDocument: '${POLICY_JSON}'
EOS
)

aws iam create-role --cli-input-yaml "$INPUT_YAML"

#===========================================================
# IAMポリシーのアタッチ
#===========================================================
INPUT_YAML=$(cat << EOS
RoleName: ${ROLE_NAME}
PolicyArn: arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${POLICY_NAME}
EOS
)

aws iam attach-role-policy --cli-input-yaml "$INPUT_YAML"
```

* **${AWS_ACCOUNT_ID}** には、対象のAWSアカウントID（数字12桁）を指定してください。
* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。
* **${REPLICATION_BUCKET_NAME}** はパラメータです。レプリケーション先のバケット名を指定します。
* **${POLICY_NAME}** はパラメータです。作成するIAMポリシー名を指定します。
* **${ROLE_NAME}** はパラメータです。作成するIAMロール名を指定します。


## バケットレベルの設定

```shell:secure_s3_bucket.sh
#===========================================================
# サーバー側の暗号化設定 (SSE-KMS)
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
ServerSideEncryptionConfiguration:
  Rules:
    - ApplyServerSideEncryptionByDefault:
        SSEAlgorithm: aws:kms
        KMSMasterKeyID: ${KMS_KEY_ID}
      BucketKeyEnabled: true
EOS
)

aws s3api put-bucket-encryption --cli-input-yaml "$INPUT_YAML"

#===========================================================
# バケットレベルのブロックパブリックアクセス設定
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
PublicAccessBlockConfiguration:
  BlockPublicAcls: true
  IgnorePublicAcls: true
  BlockPublicPolicy: true
  RestrictPublicBuckets: true
EOS
)

aws s3api put-public-access-block --cli-input-yaml "$INPUT_YAML"

#===========================================================
# ロギング設定
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
BucketLoggingStatus:
  LoggingEnabled:
    TargetBucket: ${LOGGING_BUCKET_NAME}
    TargetPrefix: ${BUCKET_NAME}
EOS
)

aws s3api put-bucket-logging --cli-input-yaml "$INPUT_YAML"

#===========================================================
# バージョニング有効化
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
VersioningConfiguration:
  Status: Enabled
EOS
)

aws s3api put-bucket-versioning --cli-input-yaml "$INPUT_YAML"

#===========================================================
# レプリケーション有効化
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
ReplicationConfiguration:
  Role: arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME}
  Rules:
    - Status: Enabled
      Priority: 1
      Filter:
        Prefix: ""
      DeleteMarkerReplication:
        Status: Disabled
      Destination:
        Bucket: arn:aws:s3:::${REPLICATION_BUCKET_NAME}
EOS
)

aws s3api put-bucket-replication --cli-input-yaml "$INPUT_YAML"

#===========================================================
# ACL無効化設定
#===========================================================
INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
OwnershipControls:
  Rules:
    - ObjectOwnership: BucketOwnerEnforced
EOS
)

aws s3api put-bucket-ownership-controls --cli-input-yaml "$INPUT_YAML"

#===========================================================
# HTTP拒否ポリシー
# 他アカウントからの機密API操作拒否ポリシー
#===========================================================
POLICY_JSON=$(cat << EOS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyInsecureRequests",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}",
                "arn:aws:s3:::${BUCKET_NAME}/*"
            ],
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": false
                }
            }
        },
        {
            "Sid": "DenySensitiveActionsFromOtherAccounts",
            "Effect": "Deny",
            "NotPrincipal": {
                "AWS": "${AWS_ACCOUNT_ID}"
            },
            "Action": [
                "s3:DeleteBucketPolicy",
                "s3:PutBucketAcl",
                "s3:PutBucketPolicy",
                "s3:PutEncryptionConfiguration",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}",
                "arn:aws:s3:::${BUCKET_NAME}/*"
            ]
        }
    ]
}
EOS
)

INPUT_YAML=$(cat << EOS
Bucket: ${BUCKET_NAME}
Policy: '${POLICY_JSON}'
EOS
)

aws s3api put-bucket-policy --cli-input-yaml "$INPUT_YAML"
```

* **${AWS_ACCOUNT_ID}** には、対象のAWSアカウントID（数字12桁）を指定してください。
* **${BUCKET_NAME}** はパラメータです。対象のバケット名を指定します。
* **${LOGGING_BUCKET_NAME}** はパラメータです。ログ保存先のバケット名を指定します。
* **${REPLICATION_BUCKET_NAME}** はパラメータです。レプリケーション先のバケット名を指定します。
* **${ROLE_NAME}** はパラメータです。作成するIAMロール名を指定します。
* **${KMS_KEY_ID}** はパラメータです。暗号化に使用する KMS Key ID を指定します。

---


# CloudFormation を捧げよ！

CloudFormation 版はこちらをご参照ください。
　↓　↓　↓

https://qiita.com/ozzy3/items/66ef9e004f44d4cd9bfa

---


# あとがき

最後まで読んでいただき、ありがとうございます。
本記事によって一匹でも多くの Security Hub 指摘を駆逐できれば幸いです。

本記事が好評でしたら、S3 以外の続編も書いていこうと思います。