レック・テクノロジー・コンサルティング株式会社TECH BLOG

※自動翻訳 / Automated translation

CSPM4製品の判定はなぜ分かれるのか、CIS AWS Foundations Benchmark v1.4.0で比較検証してみた

1. はじめに

皆さん、こんにちは。クラウド技術部のR.Aです。

昨今、設定の誤りが公開範囲や権限の問題によるセキュリティインシデントが多発しており、取引先や監査からどの基準にどこまで準拠しているかを文書で示すよう求められるようになっています。
その時に使用するのが設定をコンプライアンス基準に照らして自動で評価するCSPM製品が使われます。
今回はAWSにおける主要なCSPMである以下の4製品のCIS AWS Foundations Benchmark v1.4.0基準の出力結果を比較しました。

  1. AWS Security Hub
  2. Prowler
  3. Steampipe
  4. Service Screener V2

目次

  1. はじめに
  2. 検証環境・前提
    1. 検証環境
    2. 用語
  3. 検証内容
    1. 各製品のスキャン実行
    2. 基準の比較方法
    3. 比較マトリクス作成の流れ
  4. 検証結果
    1. 管理策カバレッジ
    2. 判定不一致
    3. リソース種別ごとの検出数
    4. 4製品すべてがFAILの31通り
  5. 分析結果
    1. 管理策カバレッジからわかること
    2. 判定不一致からわかること
    3. 検出数からわかること
    4. 全会一致FAILからわかること
    5. Service Screener V2用の対応表を作るときの注意
  6. おわりに

2. 検証環境・前提

記事中のAWSアカウントID・リソース名・リソースIDは公開にあたって別の値へ置き換えています。
引用の中の識別子も同じ規則で置き換えており、それ以外の文字列は取得した値のままです。

2.1 検証環境

表1に検証環境をまとめます。

表1: 検証環境
項目 内容
対象基準 CIS AWS Foundations Benchmark v1.4.0(全58管理策)
対象製品 AWS Security Hub / Prowler / Steampipe / Service Screener V2
対象アカウント 単一のAWSアカウント、ap-northeast-1リージョン
対象リージョン ap-northeast-1
データ取得日 4製品とも同一日
突合キー リソースのARN


その他

  1. AWS Configのレコーダーが停止していること
    • これが判定不一致を1通り生んでいます。(詳細は 5.2.5章を参照)
    • Security Hubの PASS のうち2つの管理策が判定の裏付けを持たない状態になっています。(理由コード CONFIG_EVALUATIONS_EMPTY)
  2. Service Screener V2はCIS公式の管理策番号を使わず、CloudTrail.1 や IAM.1 という独自の内部キーで結果を出すこと
    • 基準の管理策番号へ対応させる表(対応表)は自作しています。(詳細は 5.5章を参照)
    • そのため、厳密な意味での比較ではないことをご了承ください。
  3. Service Screener V2は基準のバージョンという概念が存在しないこと
    • Service Screener V2のCISという基準はあるもののv1.4.0というバージョンまでは指定されていません。
    • 今回v1.4.0として扱った理由としては、v1.4.0にあってv3.0.0以降で削除された要件への対応(CloudTrailとCloudWatch Logsの連携など)が含まれていたためです。

2.2 用語

表2にCSPMやコンプライアンス基準の文脈で一般的に使われる用語をまとめます。

表2: 一般的に使われる用語
用語 定義 備考
基準 セキュリティ設定の要求事項をまとめたもの 今回はCIS AWS Foundations Benchmark v1.4.0
管理策 基準を構成する個々の要求項目を表したもの v1.4.0には58ある。「1.9 パスワードの再利用を防ぐ」など
スキャン 各製品を1回実行すること、およびその出力のこと チェックの実行単位ではなく、実行そのものを指す
チェック 管理策を判定するために製品が実装した処理のこと Security Hubは `CloudWatch.1`、Prowlerは `iam_avoid_root_usage`。1つの管理策に複数のチェックが当たることもある
判定 チェックを実行した結果のこと PASS(問題なし)とFAIL(問題あり)のほかに、手動確認や対象外がある
Findings Security Hubが判定を1件ごとに格納して返す出力の単位のこと Security Hub独自の呼び方。ProwlerのCSVの1行、SteampipeのJSONの `controls` 配列の要素、Service Screener V2のCSVの1行が、それぞれ同じ役割を持つ
カバレッジ 製品が基準の管理策をどこまで評価できるかを表したもの 状態の種類は表4に記載する


表3にこの記事で使う用語をまとめます。
※製品側の呼び方ではなく、比較を説明するために定義しています。

表3: この記事で定義した用語
用語 定義 備考
公式定義 製品が公開している「どの管理策に自動チェックを持つか」の一覧のこと Security Hubは `describe-standards-controls`、ProwlerとSteampipeは同梱の定義ファイル
評価対象 判定が向けられているリソースやアカウント全体のこと リソース単位、アカウント単位(アカウント全体)に分類できる
比較マトリクス 4製品の判定を、管理策と評価対象の組み合わせごとに並べた表のこと 同じ管理策でも評価対象が違えば別の組み合わせになる
名寄せ 製品ごとに書き方が違うリソースの識別子をARNへ揃え、同じ対象への判定を1つの組み合わせにまとめること Service Screener V2はARNを持たないため、識別子から組み立てる
縮約 1つの管理策を複数のサブチェックで評価している製品の判定を、「1つでもFAILなら全体としてFAIL」に畳み込むこと 例えば、Prowlerがアクセスキーとコンソールパスワードを別の行で判定する場合などに縮約します
判定不一致 同じ組み合わせで2製品以上が判定を出していて、PASS/FAILの結論が異なっている状態のこと -
全会一致FAIL 同じ組み合わせで、対象の全製品がFAILと判定している状態のこと -


表4にカバレッジの状態の種類をまとめます。
この記事では、各製品のセキュリティ設定の要求事項を構成する個々の要求項目のチェックを実行した結果の評価を以下の種類に分類します。

表4: カバレッジの状態
状態 定義 備考
公式対応 公式定義にこの管理策の自動チェックがあること 環境によらず決まる
評価済 公式対応があり、今回のスキャンで判定が出たこと 環境によって変わる
手動確認 製品が「自動評価の対象外」と報告したこと 人が確認する項目
出力なし 公式対応はあるが、今回のスキャンで判定が1件も出なかったこと 対象リソースが無い場合と、製品が対象から外している場合がある
機能なし 公式定義にこの管理策のチェックが無いこと 製品がこの管理策を評価できない
評価済(裏付けなし) PASSを返しながら、判定の裏付けが無いと製品が自己申告していること Security Hubのみ。理由コード `CONFIG_EVALUATIONS_EMPTY`
要調査 公式定義に無いはずの管理策に判定が存在すること Security Hubのみ。詳細は 5.2.4章を参照


3. 検証内容

検証内容についてスキャンの条件と出力結果(各製品の判定)の突合までの詳細を記載します。

3.1 各製品のスキャン実行

各製品のスキャン実行時の条件です。

  • Prowlerには --scan-unused-services を付けていない。
    • これが検知範囲の欠落につながる(詳細は 5.3.2章を参照)
  • Security Hubは公式定義・判定ともにap-northeast-1で取得し、公式定義の取得には describe-standards-controls を使う。
  • describe-standards-controls はそのリージョンで有効な管理策しか返さない。
    • そのため、表8の公式対応38管理策はap-northeast-1で有効だったものに限られる。
表5: CIS v1.4.0の判定として扱う条件
製品 判定として扱う条件
Security Hub APIで取得したFindingsのうち、`Compliance.RelatedRequirements` の要素が `CIS AWS Foundations Benchmark v1.4.0/<管理策番号>` の形であるもの
Prowler 基準ごとにファイルが分かれるため、CIS v1.4.0向けに出力されたコンプライアンスCSVの全行
Steampipe 基準ごとに出力されるJSONを読み、各コントロールの `tags.cis_item_id` を管理策番号として使う
Service Screener V2 CISレポートのCSVのうち、`Category` と `RuleId` を連結した内部キー(例: `CloudTrail.1`)が、自作した対応表に載っているもの


前提に記載の環境に対してスキャンを行ったアカウント全体の判定と表5に記載したCIS v1.4.0だけを抽出した判定結果をまとめたものが表6です。

表6: 各製品の取得データとCIS v1.4.0の抽出結果
製品 取得したもの 全体件数 CIS v1.4.0
Security Hub Findings(APIで全件取得、72ページ) 7,124 188
Prowler 基準ごとのコンプライアンスCSV 42,783 361
Steampipe 基準ごとのJSON 68,366 439
Service Screener V2 CISレポートのCSV 38 106

表6の補足です。

  • 全体件数は製品ごとに単位が異なる
    • Security HubはFindingsの数
    • ProwlerとSteampipeは取得した全基準分の出力を合わせた判定数
    • Service Screener V2はCSVの行数で、これだけは判定の数ではない

Service Screener V2の出力は、1つの管理策に対する複数の評価対象が1行にまとめられているため、他の3製品が1行に1つの判定を書くのに対し、Service Screener V2は1行に複数を書くため、判定の単位に分けると全体件数より多くなります。

3.2 基準の比較方法

製品が提供するものと自作したものが混ざっているため、先に区別しておきます。

製品が提供するもの

  1. 各製品のスキャン出力
    1. Security HubのFindings
    2. ProwlerのCSV
    3. SteampipeのJSON
    4. Service Screener V2のCSV
  2. 各製品の公式定義
    1. Security Hubは describe-standards-controls の応答
    2. ProwlerとSteampipeは同梱の定義ファイル
    3. Service Screener V2は map.json
  3. 各製品のチェック実装のソースコード

自作したもの

  1. 突合スクリプト(表7の5段階を実行する)
  2. Service Screener V2の内部キーとCIS管理策番号の対応表(以下、対応表)
  3. 対応表とService Screener V2の map.json を突き合わせる検証スクリプト
  4. 比較マトリクス(突合スクリプトの出力)

3.3 比較マトリクス作成の流れ

製品ごとにレポートのフォーマットが違うため、加工なしに比較できません。
そのため、5段階でデータの粒度や表現をそろえて比較マトリクスを作成します。

表7: 比較の手順
段階 処理 出力
1. 抽出 各製品のレポートから管理策番号・リソース・PASS/FAILを取り出す。公式定義も別に取り出す 製品ごとの判定一覧と公式定義
2. 番号の正規化 製品ごとの管理策番号をCIS公式の番号へ揃える。Security Hubの枝番(`2.1.5.1` など)は基番号へ寄せる 同じ番号体系に揃った判定一覧
3. 名寄せ リソースの識別子をARNへ揃え、同じ対象への判定を1つの組み合わせにまとめる。ARNを持たない判定は疑似ARNを立てる 比較マトリクス(438通り)
4. 縮約 複数のサブチェックの判定を「1つでもFAILなら全体としてFAIL」に畳み込む 製品ごとに1つに定まった判定
5. 検証 比較マトリクスの全組み合わせを生データと再照合する 不整合の一覧(今回は0件)


名寄せの共通キーにARNを選んだ理由

4製品のうち3製品がARNで揃っているため、ARNを共通キーとし、Service Screener V2の Type::name を種別ごとの規則でARNへ機械的に組み立てる方針にしました。

  • Security Hubは188件中163件、Prowlerは全361件、Steampipeは438件中437件が、評価対象をARNで示している
  • Security Hubの残り25件はアカウント全体を指す AWS::::Account:123456789012 で、リソースを指す判定はARNになっている
  • Service Screener V2だけがARNを持たず、Cloudtrail::example-trail-01 のような Type::name 形式で対象を示す

4. 検証結果

検証結果の事実を記載します。分析は5章に書きます。

4.1 管理策カバレッジ

表8に、各製品がCIS v1.4.0の58管理策をどこまで対応しているか、カバレッジをまとめます。

表8: 管理策カバレッジ(全58管理策)
製品 公式対応 評価済 手動確認 出力なし 機能なし
Security Hub 38 36 0 0 19
Prowler 58 52 3 3 0
Steampipe 58 53 4 1 0
Service Screener V2 - - - - -

表8の補足です。

  • 公式対応と評価済の差は、チェックは持っているのに今回は結果が出なかった管理策となり、手動確認と出力なしに分類できます。
  • Security Hubは公式対応と評価済の差はありませんが、評価済の36の中に評価済(裏付けなし)が2つ(1.10のMFA、5.1のNetwork ACL)と表8とは別に要調査が1つ(4.3のrootアカウント使用のアラーム)があります。
    • Security Hubの管理策数 = 公式対応38 + 機能なし19 + 要調査1 = 58
  • Service Screener V2はすべて「-」としている理由
    • 自作の対応表を使って比較マトリクスを作成していますが、その他3製品が公表している情報と同じ表に記述できないと思いました。(公式情報と非公式情報のちがい)
    • 対応表の中身とその誤りは 5.5章でふれています。

4.2 判定不一致

表9に判定不一致18通りの分類をまとめます。

表9: 判定不一致18通りの分類
分類 通り 管理策 判定の並び 詳細
評価対象が0件のままPASS 12 4.3〜4.14 Service Screener V2がPASS、他3製品がFAIL 5.2.1章
しきい値の違い 1 1.9 Service Screener V2がPASS、他3製品がFAIL 5.2.2章
見ている属性の違い 1 1.12 Service Screener V2がFAIL、ProwlerがPASS 5.2.3章
1つのチェックが2つの管理策に紐づく 1 1.7 Security HubがFAIL、Prowler・SteampipeがPASS 5.2.4章
AWS Configの停止 1 3.5 Security HubがPASS、他3製品がFAIL 5.2.5章
評価スタンスの違い 2 2.1.5、3.4 製品ごとに見ている観点が違う 5.2.6章、5.2.7章

表9の補足です。

  • 分類: 異なった組み合わせを1つずつ読み、原因ごとに分けたもの
  • 通り: 比較マトリクスのうち、2製品以上が判定を出していて結論が異なった組み合わせの数

4.3 リソース種別ごとの検出数

表10にリソース種別ごとに各製品が検出したARNの数をまとめます。
判定が異なる点とは別にそもそも各製品が同じリソースを見ているかを確認するためです。

表10: リソース種別ごとの検出数
リソース種別 総数 Security Hub Prowler Steampipe Service Screener V2
セキュリティグループ 26 9 10 26 8
VPC 9 8 6 9 8
S3バケット 13 12 12 12 12
IAMユーザー 7 3 3 7 3
EC2インスタンス 5 0 5 5 0
KMSキー 4 4 4 4 3

表10の補足です。

  • 各製品の列: 比較マトリクスでその製品が判定を出しているリソースのARNの、重複を除いた数
  • 総数: 4製品が検出したARNの和集合の要素数

4.3.1 管理策5.3と3.9での検出数

表10でセキュリティグループとVPCに製品間の差が出たため、この2つを対象とする管理策5.3(デフォルトセキュリティグループ)と管理策3.9(VPCフローログ)を取り出します。
表11に各製品が判定を出した対象の数をまとめます。

表11: 管理策5.3と3.9で判定を出した対象の数
管理策 リソース種別 総数 Security Hub Prowler Steampipe Service Screener V2
5.3(デフォルトSG制限) セキュリティグループ 9 9 0 9 8
3.9(VPCフローログ) VPC 9 8 6 9 8


4.4 4製品すべてがFAILの31通り

表12に4製品すべてがFAILで一致した31通りの内訳をまとめます。
評価方式が違っても共通して検出された設定です。

表12: 全会一致FAILの内訳(合計31通り)
管理策 内容 通り
1.8 IAMパスワードポリシーの最小長14文字 1
2.1.2 S3のHTTPリクエスト拒否 9
2.1.3 S3のMFA削除 10
3.2 CloudTrailのログファイル検証 1
3.4 CloudTrailとCloudWatch Logsの連携 1
3.7 CloudTrailのKMS暗号化 1
3.8 KMS CMKのローテーション 3
3.9 VPCフローログ 5


5. 分析結果

4章に並べた表から読み取れることを表ごとにまとめます。

5.1 管理策カバレッジからわかること

製品が対応していると示す管理策の数と実際に判定が出た数は一致しませんでした。
Security Hubは58管理策のうち19に対応せず、ProwlerとSteampipeは58すべてに対応していても、評価に至ったのは52と53です。


5.1.1 Security Hubは19管理策にチェックを持たない

Security HubだけではCIS v1.4.0の58管理策のうち19を評価できません。
基準の充足を見るなら、他の製品か手動確認で補うことになります。

CIS v1.4.0の58管理策のうち、Security Hubが自動チェックを持つのは38でした。ProwlerとSteampipeは58すべてに対応しています。

Security Hubの機能なしの19管理策は次のとおりです。

  • 管理策1.1(連絡先情報の最新化)
  • 管理策1.2(セキュリティ連絡先の登録)
  • 管理策1.3(セキュリティ質問の登録)
  • 管理策1.11(初期設定でのアクセスキー作成)
  • 管理策1.13(有効なアクセスキーは1本まで)
  • 管理策1.15(グループ経由での権限付与)
  • 管理策1.18(IAMインスタンスロールの利用)
  • 管理策1.19(期限切れSSL/TLS証明書の削除)
  • 管理策1.20(IAM Access Analyzerの有効化)
  • 管理策1.21(IAMユーザーの集中管理)
  • 管理策2.1.1(S3の保存時の暗号化)
  • 管理策2.1.4(S3データの検出と分類)
  • 管理策3.10(S3の書き込みイベントのログ)
  • 管理策3.11(S3の読み取りイベントのログ)
  • 管理策4.1(認可されていないAPI呼び出しのアラーム)
  • 管理策4.2(MFAなしのサインインのアラーム)
  • 管理策4.15(AWS Organizations変更のアラーム)
  • 管理策5.2(リモート管理ポートの開放)
  • 管理策5.4(VPCピアリングのルートテーブル)

ただし、この19すべてが自動化できる要件というわけではありません。
管理策1.1と管理策1.3は、ProwlerとSteampipeも手動確認として扱っています。


5.1.2 AWS Configの停止で2つのPASSが裏付けを失っている

Security Hubの評価済36には判定の根拠を持たないPASSが2つ混ざっていました。
評価済の数はそのまま充足の証拠にはならず、実際より高く見えます。

根拠を持たない2つは理由コード CONFIG_EVALUATIONS_EMPTY が付いたPASSでした。
管理策1.10(IAMユーザーのMFA)と管理策5.1(Network ACLのリモート管理ポート)です。
AWS Configの評価結果が空のまま、判定だけがPASSになっている状態です。

原因は5.2.5章に記載するものと同じで、この環境ではAWS Configのレコーダーが停止しています。
管理策3.5(AWS Configの有効化)では Config.1 自体がPASSを返しました。
ここでは別の2管理策のPASSが根拠を失っています。
表8の評価済36にはこの2つも含まれますが、実際には評価できていません。


5.1.3 対応範囲が同じでも評価に至らない理由が違う

ProwlerとSteampipeは対応管理策数が同じ58でも評価に至った数と止まった理由が違いました。
対応管理策数は製品を比べる指標になりません。

ProwlerとSteampipeはどちらも58管理策に対応していますが、評価に至らなかった管理策の扱いが違いました。

  • Prowler: 手動確認が管理策1.1(連絡先情報の最新化)、管理策1.2(セキュリティ連絡先の登録)、管理策1.3(セキュリティ質問の登録)。出力なしが管理策1.19(期限切れSSL/TLS証明書の削除)、管理策5.3(デフォルトセキュリティグループ)、管理策5.4(VPCピアリングのルートテーブル)
  • Steampipe: 手動確認が管理策1.1、管理策1.3、管理策1.21(IAMユーザーの集中管理)、管理策5.4。出力なしが管理策1.19

両者が一致するのは3つだけです。
管理策1.1と管理策1.3はともに手動確認、管理策1.19はともに出力なしでした。

残る4つは扱いが分かれています。
管理策1.2はProwlerが手動確認で、Steampipeは判定を出しています。管理策1.21はその逆です。
管理策5.4はProwlerが出力なし、Steampipeが手動確認で、同じ管理策を別の理由で評価していません。
管理策5.3にProwlerだけ判定が無い理由は5.3.2章に記載します。

5.2 判定不一致からわかること

判定不一致18通りを1件ずつ読みましたが、事実に反している判定はありませんでした。
評価対象にするリソース、見る属性、しきい値が製品ごとに違うだけです。判定だけを並べて多数決を取る読み方ではこの違いが見えません。


5.2.1 管理策4.3〜4.14(CloudWatchのメトリクスフィルタとアラーム)

Service Screener V2は評価対象が0件の管理策を12通りPASSにしていました。
PASSだけでは問題が無いのか評価していないのかを区別できません。

12通りの内訳はすべて管理策4.3から4.14で、CloudWatchのメトリクスフィルタとアラームを見る管理策でした。
管理策4.3(rootアカウント使用のアラーム)を例に各製品の生データ(加工前)を引用します。
4.4〜4.14も同じ構造でした。

Security Hub:        FAILED     CloudWatch.1  A log metric filter and alarm should exist for usage of the "root" user
Prowler:              FAIL       cloudwatch_log_metric_filter_root_usage  No CloudWatch log groups found with metric filters or alarms associated.
Steampipe:            alarm      aws_compliance.control.cis_v140_4_3  No log metric filter and alarm exist for usage of "root" account.
Service Screener V2:  Compliant  trailWOMAroot1 / alarmsWithoutSNS

Service Screener V2だけがPASSになった理由は同じCSVの中にありました。
CloudTrailのCloudWatch Logs連携を見る CloudWatchLogsLogGroupArn が Need Attention です。
Service Screener V2自身が、このトレイルを未連携と判定しています。

未連携という判定がPASSにつながる理由は製品の map.json を見るとわかります。
trailWithoutCWLogs(CloudWatch Logs未連携のトレイル)と trailWithCWLogsWithoutMetrics(連携済みだがメトリクスが無いトレイル)が、別のチェックとして存在します。
trailWOMA で始まるチェックは後者の系統です。
この環境には連携済みのトレイルが1つも無いため、評価対象が0件のままCompliant(PASS)になったと考えられます。


5.2.2 管理策1.9(パスワード再利用防止)

この環境の再利用防止は4世代で、CIS v1.4.0が求める24世代に届いていません。
Service Screener V2だけがPASSで、しきい値そのものを見ているかどうかで判定が分かれました。

管理策1.9は、IAMのパスワードポリシーが何世代まで再利用を禁じているかを見ます。CIS v1.4.0は24世代以上を要求します。
アカウント単位の1通りで、Service Screener V2だけがPASSでした。

Security Hub:        FAILED     IAM.16  Ensure IAM password policy prevents password reuse
Prowler:             FAIL       iam_password_policy_reuse_24  IAM password policy reuse prevention is less than 24 or not set.
Steampipe:           alarm      aws_compliance.control.cis_v140_1_9  Password reuse prevention set to 4.
Service Screener V2: Compliant  passwordPolicyReuse

この環境の再利用防止は4世代で、CIS v1.4.0が要求する24世代に届いていません。Steampipeの理由文が、その値をそのまま述べています。
Service Screener V2は理由文を出しておらず、世代数を読んだうえでPASSにしたのかは出力から判断できません。
再利用防止が設定されていること自体を見ている可能性はありますが、実装は確認していないため推測です。
基準の文面に沿っているのは他の3製品です。


5.2.3 管理策1.12(45日以上未使用の認証情報)

rootアカウントはアクセスキーを持たず、コンソールパスワードは2025-06-04が最後の利用でした。
ProwlerはPASS、Service Screener V2はFAILで、同じ管理策でも見ている属性が違います。

管理策1.12はその認証情報を削除することを求めます。認証情報にはコンソールのパスワードとアクセスキーの両方が含まれます。
不一致が出たのはrootアカウントの1通りで、ProwlerがPASS、Service Screener V2がFAILでした。
Security HubとSteampipeはrootに対して判定を出していません。

Prowler:             PASS            iam_user_accesskey_unused  User <root_account> does not have access keys.
Service Screener V2: Need Attention  consoleLastAccess365  Validate IAM user console access.
Steampipe:           info            aws_compliance.control.cis_v140_1_12  <root_account> password used 04-Jun-2025, key 1 not enabled, key 2 not enabled.

Prowlerはアクセスキーの有無を見ており、rootにアクセスキーが無いためPASSにしています。
Service Screener V2はコンソールの最終アクセス日を見ており、rootのパスワードが最後に使われたのが1年以上前であるためFAILにしています。
Steampipeの理由文はこの2つの属性を同時に述べています。どちらの判定も見ている属性の範囲では事実に合っています。

なお、Service Screener V2はしきい値ごとにサブチェックを持ち、今回Need Attentionになったのは consoleLastAccess365 だけでした。
consoleLastAccess45 と consoleLastAccess90 はCompliantで、45日と90日が通って365日だけが引っかかる理由は、出力からは判断できませんでした。


5.2.4 管理策1.7(rootユーザーの利用回避)

Security Hubは1つのチェック(CloudWatch.1)を管理策1.7と管理策4.3の両方に紐づけていました。
APIと公式ドキュメントで対応が食い違うため、コントロールの一覧はAPI、そのコントロールが何を見ているかはドキュメント、と使い分けることになります。

まず、この管理策が何を求めるものかを確認します。
管理策1.7はrootを管理業務や日常業務に使わないことを求めます。
各製品の生データ(加工前)を引用します。

Security Hub: FAILED  CloudWatch.1  A log metric filter and alarm should exist for usage of the "root" user
Prowler:      PASS    iam_avoid_root_usage  Root user in the account wasn't accessed in the last 1 days.
Steampipe:    ok      aws_compliance.control.cis_v140_1_7  Root password used 04-Jun-2025 (458 days). Access Key 1 never used. Access Key 2 never used.

引用した3行は見ている属性で2つに分かれます。
Security Hubが当てている CloudWatch.1 は、rootの使用に対するメトリクスフィルタとアラームがあるかを見ます。
ProwlerとSteampipeはrootが実際に使われたかどうかを見ます。
監視体制の有無と利用実績という別の属性が、同じ管理策に当たっています。

この食い違いはAWS側にもあり、2つの情報源で対応が異なっていました。
表13にまとめます。

表13: 管理策1.7と管理策4.3に対するAWSの情報源
情報源 管理策1.7 管理策4.3
describe-standards-controls(v1.4.0) ControlId 1.7 を返す(タイトルは Eliminate use of the 'root' user for administrative and daily tasks) 返さない
AWS公式ドキュメントのマッピング表 対応するコントロールの記載なし CloudWatch.1 が対応
実際のFinding CloudWatch.1 が紐づく CloudWatch.1 が紐づく

表13の補足です。

  • describe-standards-controls(v1.4.0): CIS v1.4.0のスタンダードに対してAPIを実行した応答
  • AWS公式ドキュメントのマッピング表: Mapping of controls to CIS requirements in each version の記載
  • 実際のFinding: この環境で取得したFindingsの中身

3つの情報源は次のように違っています。

Security Hubの describe-standards-controls APIは、管理策1.7をCIS v1.4.0のコントロールとして返しますが、どのチェックを当てるかは返しません。
AWS公式ドキュメントのマッピング表では、CloudWatch.1 の対応先は管理策4.3(rootアカウント使用のアラーム)だけで、管理策1.7(rootユーザーの利用回避)に対応するコントロールの記載はありません。

一方、実際のFindingでは、CloudWatch.1 の Compliance.RelatedRequirements に管理策1.7と管理策4.3(rootアカウント使用のアラーム)の両方が入っています。

"RelatedRequirements": [
  "CIS AWS Foundations Benchmark v1.4.0/1.7",
  "CIS AWS Foundations Benchmark v1.4.0/4.3"
]

自作した突合スクリプトは公式定義をAPIから取得しているため、管理策4.3は公式定義に無いのに判定がある状態になります。
表8の要調査1つはこれで、「公式定義に無い管理策に判定が存在する」という状態を指します。


5.2.5 管理策3.5(AWS Configの有効化)

AWS Configのレコーダーが停止している環境で、Security Hubだけが Config.1 にPASSを返しました。
PASSの妥当性は理由コードかAWS Config側の recording を見ないと判断できません。

管理策3.5はAWS Configが全リージョンで有効かを見ます。
この環境ではレコーダーが停止しています。
各製品の生データ(加工前)を引用します。

Security Hub:        PASSED  Config.1  AWS Config should be enabled and use the service-linked role for resource recording
                     StatusReasons[0].ReasonCode: CONFIG_RECORDER_SERVICE_LINKED
                     StatusReasons[0].Description: AWS Config is recording all required resources using the service-linked recorder created by AWS Security Hub.
Prowler:             FAIL  config_recorder_all_regions_enabled  AWS Config recorder default is disabled.
Steampipe:           alarm  aws_compliance.control.cis_v140_3_5  ap-northeast-1 IncludeGlobalResourceTypes enabled, AllSupported enabled,
                     Recording disabled and LastStatus is SUCCESS.
Service Screener V2: Need Attention  configRecorderNotEnabled  Enable the AWS Config configuration recorder

AWS CLIの describe-configuration-recorders、describe-configuration-recorder-status、describe-delivery-channels を実行して、Security HubがPASSを返す根拠を確かめました。

describe-configuration-recorders
  name: default / roleARN: .../aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig
  allSupported: true / includeGlobalResourceTypes: true / recordingScope: PAID

describe-configuration-recorder-status
  name: default / recording: false / lastStatus: SUCCESS
  lastStartTime: 2026-01-22T15:43:10+09:00 / lastStopTime: 2026-01-22T15:50:51+09:00

describe-delivery-channels
  name: default / s3BucketName: config-bucket-123456789012

この出力結果から事前の想定が正しくないことがわかりました。
レコーダーは default の1つだけで、サービスリンクロール(AWSServiceRoleForConfig)を使っています。
Security Hub用のサービスリンクレコーダーが別に存在するという想定は誤りで、4製品とも同じ1つのレコーダーを見ていました。

理由コードの CONFIG_RECORDER_SERVICE_LINKED はレコーダーがサービスリンク型であることを指します。
Config.1 はその型の判定で通し、記録が動いているかまでは見ていない、と読めます。ただしSecurity Hub側の実装は確認していないため、この読み方は推測です。
AWS Configが実際には無効なのに Config.1 がPASSになるという状態そのものは、ライブ環境の値で確定しています。

各製品の判定がこの値と合っているかを表14にまとめます。

  • 判定: 各製品のスキャン出力
  • 整合: 上のAWS CLIの応答と1項目ずつ突き合わせた結果
表14: 各製品の判定とライブの値との整合
製品 判定 ライブの値との整合
Security Hub PASS(`CONFIG_RECORDER_SERVICE_LINKED`) 停止中のレコーダーに対してPASSを返している
Prowler FAIL 一致している
Steampipe ALARM 全リソース記録・グローバルリソース記録・記録の停止・最終ステータスの4項目とも、ライブの値どおり
Service Screener V2 FAIL 5つのサブチェックのうち `configRecorderNotEnabled` だけがNeed Attention(FAIL)。残る4つはライブの状態どおりCompliant(PASS)

Config.1 のPASSは記録が有効であることの証拠になりません。1件も記録されていない状態を見落とします。


5.2.6 管理策2.1.5(S3のパブリックアクセスブロック)

対象のS3バケットは、バケット単位の設定が無く、アカウント単位の設定だけが有効でした。
Security HubはFAIL、ProwlerとSteampipeはPASSで、どの階層の設定を見るかで判定が変わります。

管理策2.1.5で判定が割れたのは1つのS3バケット(config-bucket-123456789012)です。
Security HubだけがFAIL、ProwlerとSteampipeはPASSで一致しました。

  • S3のパブリックアクセスブロックは、バケット単位の設定が無い場合、アカウント単位の設定を継承する仕様がある
  • AWS CLIの GetPublicAccessBlock をこのバケットに対して実行すると NoSuchPublicAccessBlockConfiguration が返り、アカウントに対して実行すると4項目(BlockPublicAcls・BlockPublicPolicy・IgnorePublicAcls・RestrictPublicBuckets)とも true だった
  • CIS v1.4.0の2.1.5はバケット単位の設定を要求しているため、Security Hubの判定が基準の文面に沿っている

各製品の判定基準を引用します。

Security Hub(S3.8, FAILED): S3 general purpose buckets should block public access
Prowler(s3_bucket_level_public_access_block, PASS): Block Public Access is configured for the S3 Bucket config-bucket-123456789012 at account 123456789012 level.
Steampipe(cis_v140_2_1_5, ok): config-bucket-123456789012 all public access blocks enabled.

ProwlerとSteampipeは、アカウントレベルの設定でバケットが保護されている点を見てPASSにしています。
Prowlerの理由文にある「at account ... level」が、その根拠です。


5.2.7 管理策3.4(CloudTrailのCloudWatch Logs連携)

対象のCloudTrail証跡はCloudWatch Logsと連携していませんが、ログの記録自体は続いていました。
ProwlerはPASS、SteampipeはFAILで、設定の有無を見るか配信の実績を見るかで判定が変わります。

管理策3.4は2つの点を見ます。
CloudTrail証跡に設定されたCloudWatch LogsのロググループARN(CloudWatchLogsLogGroupArn)とそのログが実際に配信されているかです。
対象のCloudTrail証跡(example-trail-02)では、ProwlerがPASS、SteampipeがFAILでした。
Security HubとService Screener V2は判定を出していません。

Prowler(cloudtrail_cloudwatch_logging_enabled, PASS): Multiregion trail example-trail-02 has been logging in the last 24h.
Steampipe(cis_v140_3_4, alarm): example-trail-02 not integrated with CloudWatch logs.
  • Prowlerの理由文は直近のログ記録状況を見ている
  • Steampipeの理由文はCloudWatch Logsとの連携設定そのものを見ている

この連携設定が無いという事実が、5.2.1章のService Screener V2の12通りの背景にあります。1つの設定の欠落が、別の製品では12通りの不一致として現れました。

5.3 検出数からわかること

リソース種別ごとの検出数は製品間で大きく開きましたが、差は持っている管理策とその管理策の評価対象で決まっていました。
検出数の差を製品の取りこぼしと読むことはできません。


5.3.1 検出数は持っている管理策で決まる

セキュリティグループの検出数が8から26まで開いた原因は、製品の精度ではなく、持っている管理策の違いでした。
検出数の大小で、製品の優劣は測れません。

セキュリティグループは総数26に対して製品ごとの検出数が大きく違いました。差の理由は判定を出した管理策にあります。

  • Security Hub: 9。判定を出したのは管理策5.3(デフォルトセキュリティグループ)のみ
  • Prowler: 10。判定を出したのは管理策5.2(リモート管理ポートの開放)のみ
  • Steampipe: 26。判定を出したのは管理策5.2と管理策5.3
  • Service Screener V2: 8。判定を出したのは管理策5.3のみ

Security HubとService Screener V2は、管理策5.2のチェックを持ちません。
管理策5.3だけを見ているため、検出数が8から9にとどまります。
Security Hubの管理策5.2は5.1.1章に記載した機能なしの19に含まれます。

EC2インスタンスはSecurity HubとService Screener V2が0でした。
どちらも管理策1.18(IAMインスタンスロールの利用)のチェックを持たないためです。
ProwlerとSteampipeはこの管理策で5台とも評価しています。
検出数が少ないことはそのリソースを見落としたという意味ではありません。


5.3.2 Prowlerはデフォルトセキュリティグループを1つも評価していない

Prowlerは管理策5.3(デフォルトセキュリティグループ)で、対象を1つも評価していませんでした。
既定で使われていないリソースを対象から外すためで、--scan-unused-services を付けたかどうかで検知範囲が変わります。

表11では管理策5.3のProwlerが0になっています。
デフォルトセキュリティグループは全VPCに必ず1つ存在するため、対象リソースが0件では説明が付きません。

原因はProwlerのチェック実装(ec2_securitygroup_default_restrict_traffic)が持つ条件でした。
ProwlerのGitHubリポジトリから、管理策5.3の該当チェックのソースコードを引用します。

if security_group.name == "default" and (
    ec2_client.provider.scan_unused_services
    or (
        security_group.vpc_id in vpc_client.vpcs
        and vpc_client.vpcs[security_group.vpc_id].in_use
        and len(security_group.network_interfaces) > 0
    )
):

出典: ec2securitygroupdefaultrestricttraffic.py

引用したコードの条件を読み解きます。
vpc.in_use は、そのVPCまたはVPC内のサブネットにネットワークインターフェースが1件でもあるときにTrueになります。
scan_unused_services はProwlerのプロバイダオプションで、既定では無効です。
今回は --scan-unused-services を付けていないため、ネットワークインターフェースが接続されていないVPCの既定セキュリティグループが評価対象から外れました。

Prowlerの公式ドキュメントにも、既定では実際に使われているサービスだけをスキャンし、セキュリティグループはアタッチ済みのものだけを対象にする、と記載があります。

同じ条件は別の管理策でも使われています。
管理策3.9(VPCフローログ)で9個中6個しか評価されていないのも、同じ vpc.in_use の条件が使われているためです。

if vpc_client.provider.scan_unused_services or vpc.in_use:

出典: vpcflowlogs_enabled.py

この欠落は判定不一致の件数には現れません。判定不一致は両製品が判定を出した組み合わせだけを数えるため、Prowlerが判定していない組み合わせは対象外になるためです。
PASS/FAILの件数だけを足し引きした数では問題の数が実態より少なく見えます。


5.3.3 Steampipeだけが全リソースを検出している

Steampipeだけが全件を評価し、他の3製品は条件で対象を絞っていました。
棚卸しに使うならSteampipe、問題の抽出に使うなら他の製品が向きます。

Steampipeはセキュリティグループ26のうち26、IAMユーザー7のうち7と、総数と同じ数を検出しています。
他の3製品は、セキュリティグループが8から10、IAMユーザーが3でした。

Steampipeは管理策5.2(リモート管理ポートの開放)で26のセキュリティグループすべてを評価しており、対象を絞っていません。IAMユーザーも7人すべてに判定を出しています。
他の3製品がIAMユーザーを3人に絞っているのは、コンソールパスワードやアクセスキーを持つユーザーだけを対象にしているためと読めますが、実装は確認していないため推測です。


5.3.4 Service Screener V2の検出数は問題があった対象だけ

Service Screener V2の検出数は問題があった対象だけを数えたものでした。
他の3製品はPASSも含むため、検出数をそのまま並べて比べることはできません。

表10のService Screener V2の列は他の3製品と数え方が違います。

数え方の違いがはっきり出ているのが、KMSキーです。
KMSキーは総数4に対して、Security Hub・Prowler・Steampipeが4、Service Screener V2だけが3でした。
Service Screener V2が出していない1つは、他の3製品がPASS(ローテーションが有効)としているキーです。残る3つは4製品ともFAILでした。

Service Screener V2の106件の内訳は次のとおりです。

  • リソースの識別子を持つ74件はすべてNeed Attention
  • 識別子を持たない32件は、Compliantが31、Not availableが1

リソース単位で行が出るのはNeed Attentionのときだけで、問題が無いリソースは行になりません。
ローテーションが有効なKMSキーにService Screener V2の行が無いのはこのためです。
5.3.1章に記載したセキュリティグループの8も同じ理由で問題があった対象だけの数です。

そのため、表10のService Screener V2の列は問題があった対象の数であり、他の3製品の判定を出した対象の数とは意味が違います。
ただし、これは出力の並びから読み取ったもので、製品の実装は確認していません。
Compliantのリソースを行に出さないのが仕様なのか、この環境でたまたまそうなったのかは判断できません。

5.4 全会一致FAILからわかること

全会一致FAIL31通りのうち19通りが、S3バケットの2つの管理策に集中していました。
4製品の判定が揃うのは設定の有無が1つの属性で読める管理策でした。

31通りのうち19通り、全体の6割がS3バケットの2つの管理策に集中しています。
管理策2.1.3(S3のMFA削除)が10通り、管理策2.1.2(S3のHTTPリクエスト拒否)が9通りです。
どちらもバケット単位の設定が入っているかどうかだけで決まり、どの階層を見るかやしきい値の解釈が入りません。

5.2章で見た不一致はいずれも解釈の余地があるものでした。
管理策2.1.5(S3のパブリックアクセスブロック)はどの階層の設定を見るか、管理策1.9(パスワード再利用防止)はどのしきい値を使うかです。
管理策1.12(45日以上未使用の認証情報)は認証情報のどの属性を見るかでした。
判定が揃うかどうかはその管理策が1つの属性で読めるかどうかに左右されます。

5.5 Service Screener V2用の対応表を作るときの注意

自作した対応表に対象の管理策とは無関係なチェックが3件紛れ込んでいました。
うち1件は目視のレビューを通り抜けており、機械的に突き合わせる仕組みが無いと見つかりません。

Service Screener V2はCIS基準の管理策番号を持たないため、内部キー(IAM.1 や CloudTrail.5)を管理策番号へ対応させる表が要ります。
1つのRuleに複数のサブチェックが同居することがあり、どのチェックをどの管理策に当てるかを1件ずつ決めることになります。

今回はこの対応表に対象の管理策とは無関係なチェックが紛れ込んでいる箇所が3件ありました。
うち1件は目視のレビューで見落としており、製品側の map.json と全キーを機械的に突き合わせて初めて見つかりました。
自作した対応表の作り方に起因するもので、製品側の不具合ではありません。

この対応表を使って作った比較結果は対応表の精度に左右されます。
他製品の公式定義と同じ重みを与えないよう、表8ではService Screener V2の列を「-」にしています。

6. おわりに

CIS AWS Foundations Benchmark v1.4.0を4製品で突き合わせた結果をまとめます。

  • 判定が異なったのは438通りのうち18通り。うち14通りはService Screener V2が原因
  • マッピング定義を正しく直しても評価対象の範囲やしきい値が違えば結論は異なる
  • Prowlerは既定でネットワークインターフェース未接続のVPCとセキュリティグループを評価しない。--scan-unused-services を付けると対象に入る
  • Security Hubは1つのチェックを1.7と4.3の両方に紐づけており、APIの一覧と公式ドキュメントのマッピング表が異なっている
  • AWS Configが停止していてもSecurity Hubの Config.1 はPASSを返す
  • 判定不一致は両製品が判定を出した組み合わせだけを数える。片方が未検出の組み合わせは含まれないため、件数だけで一致度は測れない

CSPM製品の選定を検討している方の参考になれば幸いです。
今回の内容は弊社の検証環境における分析結果であり、環境によって結果は変わり得ることをご了承ください。
最後までご覧いただきありがとうございました!

この記事をシェアする

  • Facebook
  • X
  • Pocket
  • Line
  • Hatena
  • Linkedin

資料請求・お問い合わせはこちら

ページトップへ戻る