2022年新卒入社で現在CS課のOJTを受けていた末廣です。 Security Hubのセキュリティチェックで重大度スコア”中”以下のコントロールを無効化するスクリプトをLambdaで実装する検証を行いましたのでブログにまとめます。
Security Hub について
AWS Secyrity Hubは、AWS リソースに対して、自動化された継続的なセキュリティのベストプラクティスのチェックを行うクラウドセキュリティ体制管理サービスです。
Security Hub にはサポートされている一連のセキュリティ基準に対し、 一連の自動セキュリティ制御が提供されており、関連付けられたリソースに変更があった場合は継続して実行、もしくは設定された定期的なスケジュールで実行されます。 特定のリソースに対するセキュリティチェック項目をコントロールと呼び、 修復作業の優先順位付けに役立つ特定の重大度スコアがあります。
セキュリティ基準を有効化すると、その基準のすべてのコントロールがデフォルトで有効になり、 有効化されたコントロールから無関係なコントロールを無効化すると、 環境に無関係なコントロールのセキュリティチェックの数を減らすことができます。
概要
今回の検証目的は、コントロールの重大度スコアが一定以下のものを定期的に無効化し、 重要性が高いセキュリティチェックのコントロールのみを残すことです。 マネジメントコンソールから手動で操作することもできますが、継続的にチェックされるされるデータをわざわざ確認する作業が必要な上、 コントロールを一つ一つ無効化するのはあまりにも手間がかかります。Security Hubのアップデートによるコントロールの追加にも対応しなければいけません。 また、Security Hub はリージョン別に有効化されるため、複数リージョンでの利用やさらに複数アカウントの利用の場合、手動で今回の操作を行うのは現実的ではありません。
そこで、一日一回定時にコントロールを無効化するスクリプトをLambdaで実行されるようにします。 Lambdaを含めたリソースの作成はCloudFormationで作成します。

また、今回の検証の実行環境として、SecurityHubのサービス自体、そしていずれかのセキュリティ基準は既に有効化されていることが前提であり、 どのセキュリティ基準のコントロールを無効化するかは判別せず、全てのセキュリティ基準のコントロールを確認し、無効化する作業となります。
作成物の内容
作成したpythonスクリプト
boto3でSecurity Hubを操作する方法を順に追いながらスクリプトの説明をしたいと思います。
大まかな手順は
- 有効化されているセキュリティ基準を取得する (15~18行)
- 1.で取得したセキュリティ基準内のコントロールをリスト化 (19~33行)
- 2.のコントロールの重大度スコアが中以下のもの且つ有効化されているものをリスト化 (34~42行)
- 3.のコントロールを無効化する (43~50行)
といったものになります。リストの操作が複雑になりそうだったので2~4のステップに分けて作成しました。
import boto3 from logging import getLogger, INFO from botocore.config import Config logger = getLogger() logger.setLevel(INFO) def lambda_handler(event, context): logger.info('[START] lambda_handler') config = Config( retries=dict( max_attempts=10 ) ) securityhub = boto3.client('securityhub', config=config) # 有効化されているstandardsを取得 response = securityhub.get_enabled_standards() # describe_standards_controls実行に必要なStandardsSubscription subscription_list = response['StandardsSubscriptions'] # describe_standards_controlsで入手したControlsの部分 controls_list = [] # standardsarnごとに全てのcontrolを取得 for arn in subscription_list: token = "" # 数が多いためNextToken発生 while True: standardsctl = securityhub.describe_standards_controls( StandardsSubscriptionArn=arn['StandardsSubscriptionArn'], NextToken=token ) controls_list += standardsctl['Controls'] if 'NextToken' not in standardsctl: break token = standardsctl['NextToken'] # 無効化したい重大度スコア rating = ['MEDIUM', 'LOW'] # ratingに合致するarnのリスト controls_arn_list = [] # controlsの重大度スコアが中以下のもの、かつ有効になっているもののarnをリストに for controls in controls_list: if controls['SeverityRating'] in rating and controls['ControlStatus'] == 'ENABLED': controls_arn_list.append(controls['StandardsControlArn']) logger.info(controls['StandardsControlArn'] + 'is now enabled') # リスト化したarnをすべて無効化する for controls_arn in controls_arn_list: securityhub.update_standards_control( StandardsControlArn=controls_arn, ControlStatus='DISABLED', DisabledReason='disable under medium' ) logger.info(controls_arn + 'is disabled') logger.info('[END] lambda_handler')
ステップ 1 (15~18行)
コントロールを取得するために引数として必要となるセキュリティ基準のARNを含むレスポンスを取得するget_enable_standards
を実行します。
# 有効化されているstandardsを取得 response = securityhub.get_enabled_standards() # describe_standards_controls実行に必要なStandardsSubscription subscription_list = response['StandardsSubscriptions']
ステップ 2 (19~33行)
ステップ 1で取得した全てのセキュリティ基準のARNを引数としてコントロールを取得するdescribe_standards_controls
を実行します。
コントロールの数が多いためページネータの処理が必要なので注意です。
# describe_standards_controlsで入手したControlsの部分 controls_list = [] # standardsarnごとに全てのcontrolを取得 for arn in subscription_list: token = "" # 数が多いためNextToken発生 while True: standardsctl = securityhub.describe_standards_controls( StandardsSubscriptionArn=arn['StandardsSubscriptionArn'], NextToken=token ) controls_list += standardsctl['Controls'] if 'NextToken' not in standardsctl: break token = standardsctl['NextToken']
ステップ 3 (34~42行)
ステップ 3で有効化されているセキュリティ基準のコントロールのリストができたので、全ての重大度スコアを検査します。
rating
に無効化したいスコアを指定し且つ有効化されているコントロールのみを新しいリストに追加します。
# 無効化したい重大度スコア rating = ['MEDIUM', 'LOW'] # ratingに合致するarnのリスト controls_arn_list = [] # controlsの重大度スコアが中以下のもの、かつ有効になっているもののarnをリストに for controls in controls_list: if controls['SeverityRating'] in rating and controls['ControlStatus'] == 'ENABLED': controls_arn_list.append(controls['StandardsControlArn']) logger.info(controls['StandardsControlArn'] + 'is now enabled')
ステップ 4 (43~50行)
ステップ 3で無効化したいコントロールのリストができたので、状態を更新するメソッドupdate_standards_control
に
引数DISABLED
を与えて実行します。
# リスト化したarnをすべて無効化する for controls_arn in controls_arn_list: securityhub.update_standards_control( StandardsControlArn=controls_arn, ControlStatus='DISABLED', DisabledReason='disable under medium' ) logger.info(controls_arn + 'is disabled')
CloudFormationのYAMLファイル
作成リソースはCloudFormationで作成します。
- 上記のLambda関数
- Lambda関数へのIAMロール
- EventBridgeルール
- EventBridgeへのLambda実行権限
トリガーの設定として、毎日午前10時に起動するようにしています。 Cron形式で指定していますが、時間はUTCなので9時間前の時刻にする必要があります。
AWSTemplateFormatVersion: "2010-09-09" Parameters: ResourceName: Type: String Default: securityhub-from-lambda Resources: DisableSecurityHubLambdaFunction: Type: AWS::Lambda::Function Properties: Role: !GetAtt "LambdaExecutionRole.Arn" FunctionName: !Sub ${ResourceName}-disable-function Runtime: "python3.9" Handler: index.lambda_handler Timeout: "300" Code: ZipFile: | 略 LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: lambda-disable-securityhub-policy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - securityhub:* # - securityhub:GetEnabledStandards # - securityhub:DescribeStandardsControls # - securityhub:UpdateStandardsControl Resource: "*" EventBridgeRule: Type: AWS::Events::Rule Properties: EventBusName: default Name: !Sub ${ResourceName}-eventbridge-rule ScheduleExpression: cron(00 1 * * ? *) # 毎日10時に起動 State: ENABLED Targets: - Arn: !GetAtt DisableSecurityHubLambdaFunction.Arn Id: DisableSecurityHubLambdaFunction PermissionForEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref DisableSecurityHubLambdaFunction Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt EventBridgeRule.Arn
動作の確認
CloudWatchでLambdaが毎日10時に動作しているログが残っています。
また、重大度スコアがLow
、Medium
のコントロールが無効化されいることも確認できました。


まとめ
今回はSecurity Hubについて、重大度スコアが一定以下のコントロールを無効化することで、 より重要なものが残るような自動化スクリプトの紹介をしました。 APIを少々操作するだけのスクリプトですが、工夫を凝らすことで マルチアカウントやマルチリージョンでSecurity Hubを利用する際により活かせるのではと考えます。
末廣 満希(執筆記事の一覧)
2022年新卒入社です。ここに何かかっこいい一言を書くことができるエンジニアになれるように頑張ります。