[APIGateway.2] API ゲートウェイRESTAPIステージは、バックエンド認証にSSL証明書を使用するように設定する必要があります を CloudFormation Guard でチェックする

[APIGateway.2] API ゲートウェイRESTAPIステージは、バックエンド認証にSSL証明書を使用するように設定する必要があります を CloudFormation Guard でチェックする

Clock Icon2025.02.18

こんにちは!クラウド事業本部コンサルティング部のたかくに(@takakuni_)です。

みなさん CloudFormation テンプレートをデプロイする前に、 Security Hub の検知項目に引っかかっていないか、チェックしたいなぁと思ったことはありますでしょうか。

そういった時に、役に立つのが CloudFormation Guard や cfn-nag、Trivy、Snyk IaC などです。

AWS Foundational Security Best Practices v1.0.0 (FSBP) 標準に準拠したいケースもあるのではないでしょうか。(私です。)

そこで、今回は API ゲートウェイRESTAPIステージは、バックエンド認証にSSL証明書を使用するように設定する必要があります を CloudFormation Guard で表現してみます。

コントロールについて

今回のコントロールは REST API のステージに、クライアント証明書を設定しているかどうかをチェックするコントロールです。

設定することで、バックエンドでは API Gateway からのリクエストであるかどうかをクライアント証明書から判断できます。

個人的には少し大掛かりな気がしますが、業界や求められるセキュリティレベルによっては必要になるケースもあるのではないでしょうか。

このコントロールは、Amazon API Gateway RESTAPIステージにSSL証明書が設定されているかどうかを確認します。バックエンドシステムは、これらの証明書を使用して、受信リクエストが API Gateway からのものであることを認証します。
API ゲートウェイRESTAPIステージは、バックエンドシステムがリクエストが API Gateway から発信されたことを認証SSLできるように証明書で設定する必要があります。

https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/apigateway-controls.html#apigateway-2

作成したコード

作成したコードは以下になります。

api-gw-ssl-enabled.guard
#################################################
#                 API Gateway.02                 
#################################################
# [APIGateway.2] API Gateway REST API stages should be configured to use SSL certificates for backend authentication
# Control URL: https://docs.aws.amazon.com/securityhub/latest/userguide/apigateway-controls.html#apigateway-2
#
# Category: Protect > Data Protection > Encryption of data-in-transit
# Severity: Medium
# Resource type: AWS::ApiGateway::Stage
# AWS Config rule: api-gw-ssl-enabled
# Config Rule URL: https://docs.aws.amazon.com/config/latest/developerguide/api-gw-ssl-enabled.html

let api_gw_ssl_enabled = Resources.*[ Type == 'AWS::ApiGateway::Stage'
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "API_GW_SSL_ENABLED"
]

# AWS::ApiGateway::Stage
rule api_gw_ssl_enabled_check when %api_gw_ssl_enabled !empty {
  %api_gw_ssl_enabled.Properties {
    # ClientCertificateId が存在するかチェック
    ClientCertificateId exists
    <<
      RuleID: APIGateway.2
      Category: Protect > Data Protection > Encryption of data-in-transit
      Severity: Medium
      Resource type: AWS::ApiGateway::Stage
      Description: This control checks whether Amazon API Gateway REST API stages have SSL certificates configured. Backend systems use these certificates to authenticate that incoming requests are from API Gateway.
    >>
    # ClientCertificateId が存在し、文字列が設定されているかチェック
    when ClientCertificateId exists {
      ClientCertificateId is_string
      <<
        RuleID: APIGateway.2
        Category: Protect > Data Protection > Encryption of data-in-transit
        Severity: Medium
        Resource type: AWS::ApiGateway::Stage
        Description: This control checks whether Amazon API Gateway REST API stages have SSL certificates configured. Backend systems use these certificates to authenticate that incoming requests are from API Gateway.
      >>
    }
  }
}

証明書の設定について

クライアント証明書の設定は AWS::ApiGateway::StageClientCertificateId で行います。

template.yaml
Resources:
  Prod:
    Type: AWS::ApiGateway::Stage
    Properties:
      StageName: Prod
      Description: Prod Stage
      RestApiId: !Ref MyRestApi
      DeploymentId: !Ref TestDeployment
      DocumentationVersion: !Ref MyDocumentationVersion
      ClientCertificateId: !Ref ClientCertificate
      Variables:
        Stack: Prod
      MethodSettings:
        - ResourcePath: /
          HttpMethod: GET
          MetricsEnabled: 'true'
          DataTraceEnabled: 'false'
        - ResourcePath: /stack
          HttpMethod: POST
          MetricsEnabled: 'true'
          DataTraceEnabled: 'false'
          ThrottlingBurstLimit: '999'
        - ResourcePath: /stack
          HttpMethod: GET
          MetricsEnabled: 'true'
          DataTraceEnabled: 'false'
          ThrottlingBurstLimit: '555'

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#aws-resource-apigateway-stage--examples

そのため、AWS::ApiGateway::StageClientCertificateId が存在するか、存在している場合、求められている型と一致するかどうかがチェックロジックになります。(非常にシンプルですね。)

api-gw-ssl-enabled.guard (抜粋)
# AWS::ApiGateway::Stage
rule api_gw_ssl_enabled_check when %api_gw_ssl_enabled !empty {
  %api_gw_ssl_enabled.Properties {
    # ClientCertificateId が存在するかチェック
    ClientCertificateId exists
    <<
      RuleID: APIGateway.2
      Category: Protect > Data Protection > Encryption of data-in-transit
      Severity: Medium
      Resource type: AWS::ApiGateway::Stage
      Description: This control checks whether Amazon API Gateway REST API stages have SSL certificates configured. Backend systems use these certificates to authenticate that incoming requests are from API Gateway.
    >>
    # ClientCertificateId が存在し、文字列が設定されているかチェック
    when ClientCertificateId exists {
      ClientCertificateId is_string
      <<
        RuleID: APIGateway.2
        Category: Protect > Data Protection > Encryption of data-in-transit
        Severity: Medium
        Resource type: AWS::ApiGateway::Stage
        Description: This control checks whether Amazon API Gateway REST API stages have SSL certificates configured. Backend systems use these certificates to authenticate that incoming requests are from API Gateway.
      >>
    }
  }
}

テストしてみる

次のテストコードを用意してみました。

api-gw-ssl-enabled.yaml
# api-gw-ssl-enabled.yaml
---
# テストケース1: 空のテンプレート
# 期待値: SKIP - リソースが存在しないため、ルールはスキップされる
- name: Empty template
  input: {}
  expectations:
    rules:
      api_gw_ssl_enabled_check: SKIP

# テストケース2: 空のリソースセクション
# 期待値: SKIP - 評価対象のリソースが存在しないため、ルールはスキップされる
- name: Empty resources
  input:
    Resources: {}
  expectations:
    rules:
      api_gw_ssl_enabled_check: SKIP

# テストケース3: 有効なClientCertificateId設定
# 期待値: PASS - ClientCertificateIdが有効な文字列として設定されているため
- name: Valid ClientCertificateId
  input:
    Resources:
      MyApiStage:
        Type: AWS::ApiGateway::Stage
        Properties:
          ClientCertificateId: "abc123"
          RestApiId: !Ref MyRestApi
          StageName: prod
  expectations:
    rules:
      api_gw_ssl_enabled_check: PASS

# テストケース4: ClientCertificateId未設定
# 期待値: FAIL - 必須のClientCertificateIdが設定されていないため
- name: Missing ClientCertificateId
  input:
    Resources:
      MyApiStage:
        Type: AWS::ApiGateway::Stage
        Properties:
          RestApiId: !Ref MyRestApi
          StageName: prod
  expectations:
    rules:
      api_gw_ssl_enabled_check: FAIL

# テストケース5: ルール抑制設定
# 期待値: SKIP - Metadataでルールが明示的に抑制されているため
- name: Rule suppressed
  input:
    Resources:
      MyApiStage:
        Type: AWS::ApiGateway::Stage
        Metadata:
          guard:
            SuppressedRules:
              - "API_GW_SSL_ENABLED"
        Properties:
          RestApiId: !Ref MyRestApi
          StageName: prod
  expectations:
    rules:
      api_gw_ssl_enabled_check: SKIP

# テストケース6: 数値型のClientCertificateId
# 期待値: FAIL - ClientCertificateIdは文字列型である必要があるため
- name: Non-string ClientCertificateId
  input:
    Resources:
      MyApiStage:
        Type: AWS::ApiGateway::Stage
        Properties:
          ClientCertificateId: 12345
          RestApiId: !Ref MyRestApi
          StageName: prod
  expectations:
    rules:
      api_gw_ssl_enabled_check: FAIL

# テストケース7: 複数のステージ(混在)
# 期待値: FAIL - 1つでも非準拠のステージが存在するため、全体としてFAIL
- name: Multiple stages with mixed compliance
  input:
    Resources:
      CompliantStage:
        Type: AWS::ApiGateway::Stage
        Properties:
          ClientCertificateId: "abc123"
          RestApiId: !Ref MyRestApi1
          StageName: prod
      NonCompliantStage:
        Type: AWS::ApiGateway::Stage
        Properties:
          RestApiId: !Ref MyRestApi2
          StageName: dev
  expectations:
    rules:
      api_gw_ssl_enabled_check: FAIL

# テストケース8: 配列型のClientCertificateId
# 期待値: FAIL - ClientCertificateIdは単一の文字列である必要があるため
- name: Array type ClientCertificateId
  input:
    Resources:
      MyApiStage:
        Type: AWS::ApiGateway::Stage
        Properties:
          ClientCertificateId: ["cert1", "cert2"]
          RestApiId: !Ref MyRestApi
          StageName: prod
  expectations:
    rules:
      api_gw_ssl_enabled_check: FAIL

# テストケース9: オブジェクト型のClientCertificateId
# 期待値: FAIL - ClientCertificateIdは文字列型である必要があるため
- name: Object type ClientCertificateId
  input:
    Resources:
      MyApiStage:
        Type: AWS::ApiGateway::Stage
        Properties:
          ClientCertificateId:
            id: "cert123"
          RestApiId: !Ref MyRestApi
          StageName: prod
  expectations:
    rules:
      api_gw_ssl_enabled_check: FAIL

うまくテストケース通り通過していますね。

takakuni@ rules % cfn-guard test -r api_gateway/api-gw-ssl-enabled.guard -t api_gateway/tests/api_gateway/api-gw-ssl-enabled.yaml
Test Case #1
Name: Empty template
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = SKIP

Test Case #2
Name: Empty resources
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = SKIP

Test Case #3
Name: Valid ClientCertificateId
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = PASS

Test Case #4
Name: Missing ClientCertificateId
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = FAIL

Test Case #5
Name: Rule suppressed
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = SKIP

Test Case #6
Name: Non-string ClientCertificateId
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = FAIL

Test Case #7
Name: Multiple stages with mixed compliance
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = FAIL

Test Case #8
Name: Array type ClientCertificateId
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = FAIL

Test Case #9
Name: Object type ClientCertificateId
  PASS Rules:
    api_gw_ssl_enabled_check: Expected = FAIL

まとめ

以上、「[APIGateway.2] API ゲートウェイRESTAPIステージは、バックエンド認証にSSL証明書を使用するように設定する必要があります を CloudFormation Guard でチェックする」でした。

昨日、 API Gateway.01 を出してみたのですが、いよいよシリーズ化しそうです。

https://dev.classmethod.jp/articles/securityhub-fsbp-remediation-apigateway-1-cfn-guard/

少しずつ積み重なって、完成したら嬉しいですね。

クラウド事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.