[小ネタ]Security Hubの失敗している項目をリソース単位で取得するスクリプト(Python3)

2022.09.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Security Hubのセキュリティ基準(AWS 基礎セキュリティのベストプラクティス v1.0.0)内で失敗しているリソースの一覧を取得したくなったので、Python3(boto3)を使って取得してみました。

手っ取り早く一覧で取得したい場合にご利用ください。

スクリプト

実際のスクリプトはこちらです。Lambdaでもpyファイルとして保存しても使えます。

import logging
import boto3
import json
import pprint
from collections import defaultdict
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Security HubのFindingsから失敗しているものを取得する
def get_findings(region):
    logger.info("[START] securityhub_get_findings")
    target_findings=[]
    control_dict = defaultdict(list)
    filters = {
                "GeneratorId": [
                    {
                        "Value": "aws-foundational-security-best-practices",
                        "Comparison": "PREFIX"
                    }
                ],
                "ComplianceStatus": [
                    {
                        "Value": "FAILED",
                        "Comparison": "EQUALS"
                    }
                ],
                "RecordState": [
                    {
                        "Value": "ACTIVE",
                        "Comparison": "EQUALS"
                    }
                ],
                "WorkflowStatus": [
                    {
                        "Value": "NEW",
                        "Comparison": "EQUALS"
                    },
                    {
                        "Value": "NOTIFIED",
                        "Comparison": "EQUALS"
                    }
                ]
            }
    try:
        securityhub = boto3.client("securityhub", region_name=region)
        get_findings_paginator = securityhub.get_paginator('get_findings')
        get_findings_iterator = get_findings_paginator.paginate(
            Filters=filters,
            MaxResults=100
        )
        for r in get_findings_iterator:
            target_findings.extend(r['Findings'])

        for finding in target_findings:
            control_id = finding.get("ProductFields").get("ControlId")
            resource_id = finding.get("ProductFields").get("Resources:0/Id")
            control_dict[control_id].append(resource_id)
            
        # 重複したリソースIDを削除
        for control_id, resource_id in control_dict.items():
            resource_id = list(set(resource_id))
            control_dict[control_id] = resource_id
            
        logger.info("[END]securityhub get findings")
        control_dict=dict(control_dict)
        return control_dict

    except securityhub.exceptions.InvalidAccessException:
        msg = f"Security Hub is not activated:{region}"
        logger.error(msg)
    except Exception as e:
        logger.error(e)
        msg = f"securityhub_get_findings internal error!:{region}"
        logger.error(msg)

def lambda_handler(event, context):
    control_dict=get_findings("ap-northeast-1")
    pprint.pprint(control_dict)
    
if __name__ == "__main__":
    lambda_handler({}, {})

実行すると、以下のような結果が取得できます。

[cloudshell-user@ip-10-0-109-59 ~]$ python3 sh_get_findings.py
{'EC2.18': ['arn:aws:ec2:ap-northeast-1:111111111111:security-group/sg-0b4df856f8ef8b8fc'],
 'EC2.19': ['arn:aws:ec2:ap-northeast-1:111111111111:security-group/sg-0b4df856f8ef8b8fc'],
 'EC2.2': ['arn:aws:ec2:ap-northeast-1:111111111111:security-group/sg-0b4df856f8ef8b8fc',
           'arn:aws:ec2:ap-northeast-1:111111111111:security-group/sg-083677c10a113c13e']}

keyにコントロールのID、valueにリソースのリストが表示されます。

スクリプトの実装としてはここまでなので、Excelに書き込むなりコントロールIDを条件にフィルターをかけるなりご自由にどうぞ。

一応少しだけスクリプトの中身を説明します。

Findingsのフィルター条件

Findingsを取得する際、以下の条件でフィルターしています。

  • セキュリティ基準が「AWS 基礎セキュリティのベストプラクティス v1.0.0」
  • セキュリティチェックに合格していない
  • アクティブである(アーカイブされていない)
  • ワークフローステータスが新規・通知済み
{
    "GeneratorId": [
        {
            "Value": "aws-foundational-security-best-practices",
            "Comparison": "PREFIX"
        }
    ],
    "ComplianceStatus": [
        {
            "Value": "FAILED",
            "Comparison": "EQUALS"
        }
    ],
    "RecordState": [
        {
            "Value": "ACTIVE",
            "Comparison": "EQUALS"
        }
    ],
    "WorkflowStatus": [
        {
            "Value": "NEW",
            "Comparison": "EQUALS"
        },
        {
            "Value": "NOTIFIED",
            "Comparison": "EQUALS"
        }
    ]
}

実行リージョン

スクリプト内では実行対象のリージョンを東京にしています。

def lambda_handler(event, context):
    control_dict=get_findings("ap-northeast-1")
    pprint.pprint(control_dict)

もし別リージョンで実行したいときは、get_findingsの引数に別のリージョンを与えてください。

Security Hubの集約機能を利用している場合は、集約先のリージョンで実行すれば他リージョンの結果も含めて取得できます。

参考:【アップデート】AWS Security Hub が検出結果のリージョン集約に対応しました | DevelopersIO

おわりに

Security Hubのセキュリティチェックに失敗したリソースを取得するスクリプトを紹介してみました。

コンソールではなくスクリプトから一覧で確認したい場合にぜひご活用ください。