AWS CLIでSecurityHubの検出結果とコントールをまとめて抑制してみた

2022.10.31

こんにちは。たかやまです。

SecurityHubで検出結果の棚卸し項目の対応をする時、問題ない項目については抑制機能を使うと思います。
SecurityHubの抑制対応をコンソールからポチポチしていたのですが、件数が多いとデータ読み込みが重かったり、ページ数が多かったりと管理が大変になります。

そこで今回はAWS CLIを使ったまとめて抑制する方法をご紹介したいと思います。

検出結果単位での抑制

コンプライアンスステータスが失敗のものをCLIで抽出する

AWSコンソールでフィルターしたときの内容...

こちらの内容をCLIで抽出するときのコマンドは以下のとおりです。
クエリ部分で表示したい内容を適宜調整してください。
(CLIは見たい内容だけqueryで選択できるのがいいですね)

aws securityhub get-findings \
--filters '{"ComplianceStatus":[{"Value":"FAILED","Comparison":"EQUALS"}],"RecordState":[{"Value":"ACTIVE","Comparison":"EQUALS"}],"WorkflowStatus":[{"Value":"NEW","Comparison":"EQUALS"},{"Value":"NOTIFIED","Comparison":"EQUALS"}]}' \
--query 'sort_by(Findings,&Title)[].[Title,Resources[] | [0].Id]' \
--output table

# 出力結果
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|                                                                                                                                       GetFindings                                                                                                                                       |
+--------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|  1.1 Avoid the use of the root user                                                                          |  AWS::::Account:xxxxxxxxxxxxxx                                                                                                                                           |
|  1.10 Ensure IAM password policy prevents password reuse                                                     |  AWS::::Account:xxxxxxxxxxxxxx                                                                                                                                           |
|  CloudFormation.1 CloudFormation stacks should be integrated with Simple Notification Service (SNS)          |  arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxxxx:stack/xxxxxxxxxx-cloud9/74549430-26cb-11ed-b2dc-0a244b7eefef                                                       |
|  iam-user-group-membership-check                                                                             |  arn:aws:iam::xxxxxxxxxxxxxx:user/xxxx-user                                                                                                                              |
+--------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

コンプライアンスステータスが失敗のものをまとめて抑制する

検出結果を確認して問題ない場合、まとめて抑制したいケースがあります。
コンソールでも複数選択でまとめて抑制可能ですが最大20件しか選択できないため、20件以上ある場合には複数回繰り返す必要があります。

以下のコマンドでは、先程抽出したコンプライアンスステータスが失敗のものをまとめて抑制するコマンドになります。

aws securityhub get-findings \
--filters '{"ComplianceStatus":[{"Value":"FAILED","Comparison":"EQUALS"}],"RecordState":[{"Value":"ACTIVE","Comparison":"EQUALS"}],"WorkflowStatus":[{"Value":"NEW","Comparison":"EQUALS"},{"Value":"NOTIFIED","Comparison":"EQUALS"}]}' \
--query 'sort_by(Findings,&Id)[].[Id,ProductArn]' \
--output text \
| while read finding_id product_arn; do
    aws securityhub batch-update-findings \
    --finding-identifiers Id=${finding_id},ProductArn=${product_arn} \
    --workflow Status='SUPPRESSED' \
    --query 'ProcessedFindings[].Id' \
    --output text
done

ワークフローステータスで抑制しているものをCLIで抽出する

抑制した後、抑制済みのものを確認する際には以下のコマンドを実施します。
このとき、こちらのブログのようにNoteを記載していると、あとの確認で抑制した理由がわかりやすくなるのでおすすめです。

aws securityhub get-findings \
--filters '{"RecordState":[{"Value":"ACTIVE","Comparison":"EQUALS"}],"WorkflowStatus":[{"Value":"SUPPRESSED","Comparison":"EQUALS"}]}' \
--query 'sort_by(Findings,&Title)[].[Title,Resources[] | [0].Id,Note.Text]' \
--output table

# 出力結果
# Noteを記載していないものはNoneで出力される
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|                                                                                                                                                          GetFindings                                                                                                                                                         |
+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------+
|  CloudFront.4 CloudFront distributions should have origin failover configured                          |  arn:aws:cloudfront::xxxxxxxxxxxx:distribution/E16UxxxxxVS1BZ                                                                      |  オリジンはS3を採用しておりかつフェイルオーバーを実装するほどではないため抑制  |
|  DynamoDB.1 DynamoDB tables should automatically scale capacity with demand                            |  arn:aws:dynamodb:ap-northeast-1:xxxxxxxxxxxx:table/xxxxxxxxxxxxx-aha-DynamoDBTable-197F6M18IOT28                                  |  AHAで作成されるリソースのため                                                 |
|  KMS.2 IAM principals should not have IAM inline policies that allow decryption actions on all KMS keys|  arn:aws:iam::xxxxxxxxxxxx:role/cdk-hnb659fds-deploy-role-xxxxxxxxxxxx-ap-northeast-1                                              |  CDKで作成されるリソースのため                                                 |
|  SNS.2 Logging of delivery status should be enabled for notification messages sent to a topic          |  arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:StorageStack-xxxxxxx                                                                      |  None                                                                          |
+--------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------+

コントロール単位での抑制

さきほどは検出結果単位で抑制していましたが、今度はコントロール単位の抑制方法をご紹介します。

全リージョンのコントロールをまとめて無効する

アカウント内で抑制しても問題ないコントロールがあった場合、コントロール単位の場合、SecurityHubを有効化しているリージョンごとに無効化対応をする必要があります。
以下のコマンドでは指定したコントロールを全リージョンで無効化する処理を行います。

control_ids=( # 無効対象のコントロールIDを記載する(複数可)
  CloudFormation.1
  S3.1
)
reasons=( # 無効理由を記載(複数可) ※コントロールIDの対になる順番で記載すること
  CloudFormation通知の対応は必須ではないため無効化
  バケットレベルでブロックパブリックアクセスを有効にするため無効化
)
account_id=$(aws sts get-caller-identity --query Account --output text)
aws ec2 describe-regions --query Regions[].[RegionName] --output text \
| while read region; do
    echo "### ${region} ###";
    for ((i=0; i<${#control_ids[@]}; i++)); do
      aws securityhub update-standards-control --standards-control-arn arn:aws:securityhub:${region}:${account_id}:control/aws-foundational-security-best-practices/v/1.0.0/${control_ids[i]} \
      --control-status DISABLED \
      --disabled-reason ${reasons[i]} \
      --region ${region}
    done
  done

# 実行結果 リージョンによっては存在しないコントロールもあるのでエラーあり
### eu-north-1 ###

An error occurred (ResourceNotFoundException) when calling the UpdateStandardsControl operation: StandardsControl not found
### ap-south-1 ###
### eu-west-3 ###

An error occurred (ResourceNotFoundException) when calling the UpdateStandardsControl operation: StandardsControl not found
### eu-west-2 ###

コントロールステータスで無効にしているものをCLIで抽出する

無効化済みのコントロールを確認はこちらのコマンドを実行します。

account_id=$(aws sts get-caller-identity --query Account --output text)
aws ec2 describe-regions --query Regions[].[RegionName] --output text \
| while read region; do
    echo "### ${region} ###";
    aws securityhub describe-standards-controls \
    --standards-subscription-arn arn:aws:securityhub:${region}:${account_id}:subscription/aws-foundational-security-best-practices/v/1.0.0 \
    --query 'sort_by(Controls,&ControlId)[?ControlStatus==`DISABLED`].[ControlId,ControlStatus,DisabledReason]' \
    --output table \
    --region ${region}
done

#実行結果
### eu-north-1 ###
--------------------------------------------------------------------------------------------------
|                                    DescribeStandardsControls                                   |
+--------------+-----------+---------------------------------------------------------------------+
|  CloudTrail.5|  DISABLED |  Classmethod SecureAccount Settings                                 |
|  Config.1    |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  EC2.8       |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  IAM.6       |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  S3.1        |  DISABLED |  バケットレベルでブロックパブリックアクセスを有効にするため無効化   |
+--------------+-----------+---------------------------------------------------------------------+
### ap-south-1 ###
------------------------------------------------------------------------------------------------------
|                                      DescribeStandardsControls                                     |
+------------------+-----------+---------------------------------------------------------------------+
|  CloudFormation.1|  DISABLED |  CloudFormation通知の対応は必須ではないため無効化                   |
|  CloudTrail.5    |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  Config.1        |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  EC2.8           |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  IAM.6           |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  S3.1            |  DISABLED |  バケットレベルでブロックパブリックアクセスを有効にするため無効化   |
+------------------+-----------+---------------------------------------------------------------------+
### eu-west-3 ###
--------------------------------------------------------------------------------------------------
|                                    DescribeStandardsControls                                   |
+--------------+-----------+---------------------------------------------------------------------+
|  CloudTrail.5|  DISABLED |  Classmethod SecureAccount Settings                                 |
|  Config.1    |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  EC2.8       |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  IAM.6       |  DISABLED |  Classmethod SecureAccount Settings                                 |
|  S3.1        |  DISABLED |  バケットレベルでブロックパブリックアクセスを有効にするため無効化   |
+--------------+-----------+---------------------------------------------------------------------+
### eu-west-2 ###
...

最後に

件数が多いとAWSコンソールだけで対応するのはきついものがありましたが、AWS CLIを使うことでSecurity Hubの棚卸しがちょっと楽にできると思います。
ぜひ、データ件数が多い場合にはAWS CLIをご活用いただければと思います。

やりすぎない程度に抑制機能を活用して、SecurityHub 100%を目指していきましょう!

以上、たかやま(@nyan_kotaroo)でした。