vegazbabz/CISAzureBenchmark-PS

GitHub: vegazbabz/CISAzureBenchmark-PS

该工具是基于 PowerShell 的 Azure 云环境安全合规审计器,用于自动化执行 CIS Azure Foundations Benchmark 检查并生成可视化报告。

Stars: 1 | Forks: 0

# CIS Microsoft Azure Foundations Benchmark v6.0.0 — 审计工具 (PowerShell) [![PowerShell Gallery](https://img.shields.io/powershellgallery/v/CISAzureFoundationsBenchmark?label=PowerShell%20Gallery)](https://www.powershellgallery.com/packages/CISAzureFoundationsBenchmark) [![Gallery downloads](https://img.shields.io/powershellgallery/dt/CISAzureFoundationsBenchmark?label=downloads)](https://www.powershellgallery.com/packages/CISAzureFoundationsBenchmark) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) [![CIS Benchmark](https://img.shields.io/badge/CIS%20Benchmark-v6.0.0-blue.svg)](https://www.cisecurity.org/benchmark/azure) [![PowerShell](https://img.shields.io/badge/PowerShell-7.0%2B-blue.svg)](https://learn.microsoft.com/en-us/powershell/) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/vegazbabz/CISAzureBenchmark-PS/actions/workflows/ci.yml) ![示例报告仪表盘](https://static.pigsec.cn/wp-content/uploads/repos/cas/49/493561d9e773dc1e28594f86db157b0e5a9b8e5ced3bb9d652de42c49619063c.png) **版本:** 2.4.0 **Benchmark:** [CIS Microsoft Azure Foundations Benchmark v6.0.0](https://www.cisecurity.org/benchmark/azure) (2026年4月) **覆盖范围:** 涵盖 7 个部分的 93 项自动化控制 · 输出中注明的 34 项手动控制(共 127 项) ## 概述 这是一款 PowerShell 工具,用于根据 **[CIS Microsoft Azure Foundations Benchmark v6.0.0](https://www.cisecurity.org/benchmark/azure)** —— 由[互联网安全中心 (CIS)](https://www.cisecurity.org/) 发布的 Azure 环境行业标准强化指南,对 Azure 租户进行审计。 所有审计检查均使用 Az PowerShell 模块 —— 无需 Azure CLI。 可选的权限预检会在并行 runspace 中使用 `Get-AzRoleAssignment`,以在审计开始前验证运行者账户是否持有所需的角色。 如果您已确认权限无误,可以使用 `-NoPermissionCheck` 跳过此步骤。 只需一条 `Install-Module CISAzureFoundationsBenchmark` 命令即可引入该工具及其所需的所有 Az 模块;运行 `Connect-AzAccount` 进行身份验证。 每个订阅完成后,结果都会作为检查点保存,因此如果运行失败或中断,可以恢复执行而无需重新运行已完成的工作。输出是一个独立的 HTML 报告,包含过滤、合规性评分、图表以及针对各项发现的修复指南。 ## 环境要求 ### 运行时 | 要求 | 详情 | | --- | --- | | PowerShell | [7.0 或更高版本](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell) | | Az.Accounts | `Install-Module Az.Accounts` —— 身份验证、上下文、REST 调用 | | Az.ResourceGraph | `Install-Module Az.ResourceGraph` —— 资源预取查询 | | Az.Monitor | `Install-Module Az.Monitor` —— 日志配置文件、诊断设置、活动日志警报 | | Az.Network | `Install-Module Az.Network` —— 网络观察程序流日志 | | Az.Storage | `Install-Module Az.Storage` —— 存储账户枚举 | | Az.KeyVault | `Install-Module Az.KeyVault` —— 密钥轮换策略 | | Az.Resources | `Install-Module Az.Resources` —— 角色定义 | | Az.Security | `Install-Module Az.Security` —— Defender 计划、安全联系人 | | Azure 登录 | 运行前需完成 `Connect-AzAccount` | ### Azure 权限 | 范围 | 角色 | 用途 | | --- | --- | --- | | 每个订阅 | Reader | 枚举所有资源 | | 每个订阅 | Security Reader | Defender 计划、安全联系人 | | Microsoft Entra ID (租户) | Global Reader | 身份检查 (5.x) | | Key Vaults (可选) | Key Vault Reader | 列出用于 8.3.x 检查的密钥、机密、证书 | ## 快速开始 ``` # 1. 安装(一次性) — 所需的 Az modules 会自动安装 Install-Module CISAzureFoundationsBenchmark -Scope CurrentUser # 2. 登录到 Azure Connect-AzAccount # 3. 运行审计(审计您的 tenant 中所有已启用的 subscriptions) Invoke-CISAzureAudit -TenantId (Get-AzContext).Tenant.Id ``` 审计完成后,报告会自动在浏览器中打开。该工具将枚举租户中所有已启用的订阅,运行所有检查,并保存报告。 (需要显式指定 `-TenantId` 或 `-Subscriptions` 范围 —— 工具不会自动假定范围。) 如果是通过克隆的代码库运行?可以使用脚本入口点,其行为完全相同: `.\Invoke-CISAzureAudit.ps1 -TenantId (Get-AzContext).Tenant.Id` ### 作为 PowerShell 模块使用 该工具也是一个正规的模块 —— 导入它并处理返回的摘要对象,而无需解析控制台输出: ``` Import-Module CISAzureFoundationsBenchmark # from a clone: Import-Module .\CISAzureFoundationsBenchmark.psd1 $audit = Invoke-CISAzureAudit -TenantId (Get-AzContext).Tenant.Id -NoOpen $audit.Score # e.g. 62.1 $audit.Counts.FAIL # failed control count $audit.Results # every per-resource result object $audit.ReportPath # path to the generated HTML report $audit.ExitCode # 0 | 1 (setup error) | 2 (failures found, with -ExitCode) ``` `.\Invoke-CISAzureAudit.ps1` 脚本依然是此函数的轻量级封装,因此现有的命令行和 CI pipeline 可保持不变地继续工作。 ## 详细入门步骤 ### 步骤 1 — 安装 PowerShell 7+ ### 步骤 2 — 安装所需的 Az PowerShell 模块 **如果您是通过 PowerShell Gallery 安装的,请跳过此步骤**(步骤 3,选项 A)—— Az 模块是声明的依赖项,会自动附带安装。 对于克隆或 ZIP 下载的代码,请在 PowerShell 7 终端中运行一次此命令: ``` Install-Module Az.Accounts, Az.ResourceGraph, Az.Monitor, Az.Network, Az.Storage, Az.KeyVault, Az.Resources, Az.Security -Scope CurrentUser ``` 如果提示从不受信任的存储库安装,请输入 `Y` 并按 Enter。 ### 步骤 3 — 获取工具 **选项 A — 从 PowerShell Gallery 安装**(推荐): ``` Install-Module CISAzureFoundationsBenchmark -Scope CurrentUser ``` **选项 B — 使用 Git 克隆**: ``` git clone https://github.com/vegazbabz/CISAzureBenchmark-PS.git cd CISAzureBenchmark-PS ``` **选项 C — 以 ZIP 格式下载**(无需 Git): 1. 在 [GitHub 代码库页面](https://github.com/vegazbabz/CISAzureBenchmark-PS), 点击绿色的 **Code** 按钮 → **Download ZIP**。 2. 解压 ZIP 文件并在解压后的文件夹中打开终端。 ### 步骤 4 — 登录 Azure ``` Connect-AzAccount ``` 将打开一个浏览器窗口供您登录。您使用的账户至少需要对要审计的订阅拥有 **Reader** 和 **Security Reader** 权限。 ### 步骤 5 — 运行审计 ``` Invoke-CISAzureAudit -TenantId (Get-AzContext).Tenant.Id ``` (从克隆的代码库运行时,`.\Invoke-CISAzureAudit.ps1 -TenantId (Get-AzContext).Tenant.Id` 的作用完全相同。)该工具将枚举所有已启用的订阅,运行所有检查,并在完成后自动在浏览器中打开 HTML 报告。 ## 项目结构 ``` CISAzureFoundationsBenchmark.psd1 Module manifest (version, exports, Gallery metadata) CISAzureFoundationsBenchmark.psm1 Module loader — dot-sources Private/, Checks/, Public/ Invoke-CISAzureAudit.ps1 Script entry point (thin shim around the module function) Public/ Invoke-CISAzureAudit.ps1 The audit orchestrator — the module's exported command Private/ AzureClient.ps1 PS-based API client: Resource Graph (Search-AzGraph) and ARM REST (Invoke-AzRestMethod) CheckHelpers.ps1 Prefetch data lookups, error formatting, New-ErrorResult / New-InfoResult / New-ManualResult Checkpoint.ps1 Save/resume audit state Config.ps1 Timeouts, constants, PASS/FAIL labels Helpers.ps1 Logging, utilities History.ps1 Run history tracking Identity.ps1 Subscription enumeration, permission checks Models.ps1 New-CISResult (the result object contract) ModuleManifest.ps1 Single source of truth for the dot-source load order Report.ps1 HTML report generation Sarif.ps1 SARIF 2.1.0 export for code scanning Checks/ Section2.ps1 Databricks checks (12 controls) Section3.ps1 Compute checks (1 manual control) Section5.ps1 Identity & access checks (15 controls) Section6.ps1 Logging & monitoring checks (24 controls) Section7.ps1 Networking checks (16 controls) Section8.ps1 Security services checks (38 controls) Section9.ps1 Storage checks (21 controls — 127 total) Tests/ Section*.Tests.ps1 Pester tests per benchmark section (314 tests total) Helpers.Tests.ps1 Pure-helper tests (factories, catalog, scoring, classifiers) Pipeline.Tests.ps1 Report/summary pipeline tests (SARIF, suppressions, history) TestHelpers.ps1 Shared bootstrap: fixtures + hermetic default mocks Run-Tests.ps1 Test runner scripts/ New-SampleReport.ps1 Generate sample report with synthetic data docs/ sample_report.html Pre-generated sample report suppressions.json.example Suppression template with annotated examples ``` ## 用法 ``` .\Invoke-CISAzureAudit.ps1 [options] ``` ### 所有参数 | 参数 | 类型 | 默认值 | 描述 | | --- | --- | --- | --- | | `-Subscriptions` | string[] | all | 要审计的一个或多个订阅名称或 ID | | `-Output` | string | `cis_audit_report.html` | HTML 报告路径 | | `-Parallel` | int | 3 | 并发订阅 worker 数 | | `-Level` | 1 \| 2 \| both | both | CIS 级别过滤器 | | `-Fresh` | switch | | 清除所有检查点并开始完整的重新审计 | | `-ReportOnly` | switch | | 从现有检查点重新生成报告 —— 不进行 API 调用 | | `-NoCheckpoint` | switch | | 禁用检查点保存 | | `-SkipTenantChecks` | switch | | 跳过租户级别的检查(第 3 和第 5 部分) | | `-NoPermissionCheck` | switch | | 跳过预检权限检查 | | `-NoOpen` | switch | | 不在浏览器中自动打开报告 | | `-ExitCode` | switch | | 发现 FAIL 或 ERROR 结果时以代码 2 退出(用于 CI/CD) | | `-SuppressionsFile` | string | `suppressions.json` | 抑制文件的路径(参见 *抑制发现*) | | `-CompareWith` | string | | 用于进行差异比较的前一次运行的 `.json`,或设为 `auto` 以选取输出目录中最新的报告 JSON。这会在 HTML 报告中添加“与上一次运行的变化”部分,并写入 `.diff.json` | | `-DebugMode` | switch | | 详细的调试日志 | | `-LogFile` | string | | 将日志写入文件 | ### 配置文件 在脚本旁边创建一个 `cis_audit.json` 文件以设置持久化默认值。CLI 参数始终会覆盖配置文件中的值。设置 `CIS_AUDIT_CONFIG` 环境变量可使用自定义路径。 有关所有可用设置,请参见 [`cis_audit.json.example`](cis_audit.json.example)。 ``` { "audit": { "parallel": 3, "level": "both", "exit_code": true, "no_open": true }, "timeouts": { "default": 60, "storage_list": 90 } } ``` ### 示例 ``` # 审计所有 subscriptions .\Invoke-CISAzureAudit.ps1 # 使用并行机制审计特定 subscriptions .\Invoke-CISAzureAudit.ps1 -Subscriptions "sub-id-1","sub-id-2" -Parallel 5 # 仅执行 Level 1 检查,自定义输出路径 .\Invoke-CISAzureAudit.ps1 -Level 1 -Output report.html # 完全重新开始,忽略之前的 checkpoints .\Invoke-CISAzureAudit.ps1 -Fresh # 重新生成 HTML 报告,无需重新运行任何检查 .\Invoke-CISAzureAudit.ps1 -ReportOnly # 将 Trace 级别的诊断信息写入日志文件 .\Invoke-CISAzureAudit.ps1 -DebugMode -LogFile cis_audit.log # 跳过 tenant 级别的身份检查 .\Invoke-CISAzureAudit.ps1 -SkipTenantChecks # 运行中断了?只需重新运行 — 它会自动恢复 .\Invoke-CISAzureAudit.ps1 # 显示当前被抑制的发现结果 .\Invoke-CISAzureAudit.ps1 -ReportOnly # suppressions applied during report generation # 与上一次运行进行 Diff(regressions、improvements、新增/移除的结果) .\Invoke-CISAzureAudit.ps1 -CompareWith auto ``` ## 工作原理 ### 数据收集 —— 三种方法 #### 1. Azure Resource Graph(批量预取 —— 每次审计一次) 在任何针对特定订阅的工作开始之前,Kusto 查询会通过一次往返获取整个租户内的所有相关资源: - 网络安全组 (NSG) 和安全规则 - 存储账户和安全属性 - Key Vault —— 访问配置和网络设置 - 虚拟网络、子网和 NSG 关联 - 应用程序网关和 WAF 设置 - Databricks 工作区 - Bastion Host - 网络观察程序和资源位置 - 角色分配(Owner 和 User Access Administrator) - WAF 策略 #### 2. 每个订阅的 Az PowerShell 模块调用 用于获取 Resource Graph 无法公开的实时服务配置和数据: - `Get-AzSecurityPricing` —— Defender 计划状态 (8.1.x) - `Get-AzActivityLogAlert` —— 所有 11 项警报检查 (6.1.2.x) - `Get-AzDiagnosticSetting` —— Key Vault 和应用服务诊断日志 (6.1.1.4, 6.1.1.6) - `Get-AzLogProfile` —— 通过日志配置文件保留活动日志 (6.1.1.3) - `Get-AzNetworkWatcherFlowLog` —— 流日志保留期 (7.5, 7.8) - `Get-AzKeyVaultKey` / `Get-AzKeyVaultSecret` / `Get-AzKeyVaultCertificate` —— 过期日期 (8.3.x) - `Get-AzKeyVaultKeyRotationPolicy` —— 自动轮换配置 (8.3.9) - `Get-AzStorageBlobServiceProperty` —— 软删除、版本控制、日志记录 (9.2.x) - `Get-AzStorageFileServiceProperty` —— 文件软删除和 SMB 设置 (9.1.x) - `Get-AzStorageAccount` —— 存储安全属性 (9.3.x) - `Get-AzResourceLock` —— CanNotDelete / ReadOnly 锁 (9.3.9, 9.3.10) - `Get-AzRoleDefinition` —— 自定义管理员角色检测 (5.4) #### 3. 通过 `Invoke-AzRestMethod` 进行 ARM / Microsoft Graph REST 调用 用于 Az 模块 cmdlet 未公开的租户级身份检查和 API: - `graph.microsoft.com/v1.0/policies/identitySecurityDefaultsEnforcementPolicy` —— 安全默认值 (5.1.1) - `graph.microsoft.com/v1.0/identity/conditionalAccess/policies` —— 条件访问回退 (5.1.1) - `graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails` —— 每用户 MFA 注册 (5.1.3) - `management.azure.com/.../diagnosticSettings` —— 订阅级活动日志路由 (6.1.1.x) - 用于安全联系人 (8.1.12–8.1.15)、WDATP 集成 (8.1.3.3) 和攻击路径通知 (8.1.15) 的 ARM REST #### 4. 权限预检 审计开始前,会在并行 runspace 中(最多同时 8 个)调用 `Get-AzRoleAssignment`,以验证运行者账户在每个订阅上是否持有所需的角色。无需 Azure CLI。使用 `-NoPermissionCheck` 可完全跳过预检。 ### 权限预检 审计开始前,该工具会检查运行者账户是否在每个订阅上持有所需的角色。 ### 检查点与恢复 每个订阅完成后,结果会写入 `cis_checkpoints/.json`。 如果脚本在运行中途停止或崩溃,重新运行它将跳过已完成的订阅,并从上次中断的地方继续。使用 `-Fresh` 可放弃所有检查点并重新开始。 ### 并行执行 订阅通过 PowerShell runspace 并发运行。默认值为 3 个并行 worker(可通过 `-Parallel` 配置)。Resource Graph 预取始终在并行循环开始前运行一次。 内置了自适应并发:如果 Azure 返回限制响应 (HTTP 429),工具会自动减少并发 worker 数量。在连续两批没有被限制后,worker 数量会增加回原始值。 ## HTML 报告 生成的报告是一个独立的 HTML 文件,没有外部依赖。 - **摘要卡片** —— 合规性评分(PASS / (PASS + FAIL + ERROR) —— 无法读取的资源会扣分;INFO、MANUAL 和 SUPPRESSED 状态会被排除),以及每种状态的计数。 - **合规性圆环图** —— 三个环状图,分别显示总体、Level 1 和 Level 2 的 PASS/FAIL/ERROR 比例。 **部分细分** —— 每个 CIS 部分的水平堆叠条形图,从最差到最好排序。 - **按订阅汇总** —— 堆叠条形图表格,显示每个订阅的通过/失败/错误计数;点击某一行可按该订阅过滤结果表。 - **可过滤表格** —— 可同时通过自由文本搜索、订阅、状态和级别 (L1/L2) 进行过滤。当所有结果都被过滤掉时,部分标题会折叠。 - **按资源的结果** —— 每个 NSG、存储账户、Key Vault、子网和 Databricks 工作区都会单独报告,而不是汇总为单个控制的通过/失败。 - **修复提示** —— 每个 FAIL 结果都包含修复问题的 Azure 门户导航路径。ERROR 结果包含关于缺少哪些访问权限的可操作说明。 - **审计错误行** —— 如果订阅上下文切换、整个检查组或租户级别的运行崩溃,将生成一个合成的 ERROR 行(控制项为位于 *0 - Audit Errors* 部分下的 `CONTEXT`、`GROUP`、`TENANT` 或 `FATAL`),以便在报告中显示失败,而不是让控制项静默消失。 - **合规性趋势** —— 经过两次或多次完整租户审计运行后,将出现一个显示合规性评分随时间变化的图表,该图表可折叠。 - **返回顶部** —— 长报告右下角的固定按钮。 ### 状态类型 | 状态 | 含义 | | --- | --- | | PASS | 控制项合规 | | FAIL | 控制项不合规 —— 提供修复提示 | | ERROR | 检查无法完成 —— 审计缺口(缺少权限、超时或 API 错误)。合规性**未知**;不应视为合规。 | | INFO | 不适用 —— 资源类型不存在或账户类型不支持该功能 | | MANUAL | 无法自动化 —— 需要根据 CIS PDF 进行手动验证 | | SUPPRESSED | 已接受的风险 —— 已确认并附带理由的发现 | ### `-ReportOnly`:在不重新审计的情况下重新生成报告 ``` .\Invoke-CISAzureAudit.ps1 -ReportOnly ``` ### 输出文件 每次运行都会在 HTML 报告旁边生成四个文件: | 文件 | 格式 | 用途 | | --- | --- | --- | | `cis_audit_report.html` | HTML | 交互式可视化报告(除非使用 `-NoOpen`,否则自动打开) | | `cis_audit_report.json` | JSON | 供下游工具使用的机器可读结果 | | `cis_audit_report.csv` | CSV | 适合合规团队的电子表格格式 | | `cis_audit_report.sarif` | SARIF 2.1.0 | 用于 GitHub 代码扫描 / Defender for DevOps 的发现(参见 *CI/CD pipeline*) | 使用 `-Output report.html` 更改基础路径 —— `.json`、`.csv` 和 `.sarif` 扩展名将自动派生。 SARIF 日志仅包含发现:FAIL 映射为 `error`,ERROR 映射为 `warning`(无法审计的控制项也是一种发现,映射反映在评分中),而 SUPPRESSED 结果会带有 SARIF 抑制对象,以便代码扫描 UI 将其显示为已驳回。PASS/INFO/MANUAL 将被忽略。 ## 抑制发现(已接受的风险) 在项目根目录下创建一个 `suppressions.json` 文件,以将特定发现标记为已接受的风险。 被抑制的发现将在报告中重新分类为 **SUPPRESSED**,并从 FAIL/ERROR 计数中排除。 [`suppressions.json.example`](suppressions.json.example) 中提供了一个带有注释示例的模板 (包含由 WAF 架构和 B2B 来宾管理员账户的示例)。 将其复制到 `suppressions.json` 并删除不适用的条目。 ### suppressions.json 格式 ``` [ { "control_id": "9.5", "justification": "Legacy storage account — migration planned for Q3", "expires": "2025-09-30", "resource": "/subscriptions/.../storageAccounts/legacy01", "subscription": "Production" } ] ``` ### 抑制规则 - `control_id`(必填)—— 要抑制的 CIS 控制 ID(例如 `"9.5"`)。 - `justification`(必填)—— 抑制的人类可读理由。 - `expires`(必填)—— 以 `YYYY-MM-DD` 格式表示的过期日期。距离今天最多 365 天。 - `resource`(可选)—— 要匹配的资源 ID。省略此选项可抑制此控制项的所有资源。 - `subscription`(可选)—— 订阅名称或 ID。省略此选项可跨所有订阅进行抑制。 过期的条目将被静默忽略。摘要横幅会显示处于活动状态的抑制计数。 ## 覆盖的控制项 ### 第 2 部分 —— Azure Databricks(6 项自动化 · 6 项手动) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 2.1.1 | 部署在客户管理的 VNet 中的 Databricks | L1 | | 2.1.2 | 为 Databricks 子网配置了 NSG | L1 | | 2.1.3 | 集群工作节点之间的流量已加密(手动) | L2 | | 2.1.4 | 从 Microsoft Entra ID 同步的用户/组(手动) | L1 | | 2.1.5 | 配置了 Unity Catalog(手动) | L1 | | 2.1.6 | PAT 使用受限且强制执行过期(手动) | L1 | | 2.1.7 | 配置了诊断日志传送 | L1 | | 2.1.8 | 关键数据使用客户管理的密钥加密(手动) | L2 | | 2.1.9 | 启用了 No Public IP | L1 | | 2.1.10 | 禁用了 Allow Public Network Access | L1 | | 2.1.11 | 使用专用终结点访问工作区 | L2 | | 2.1.12 | 定期审查 Databricks 组(手动) | L1 | ### 第 3 部分 —— 计算服务(1 项手动) | 控制项 | 标题 | 级别 | 注意 | | --- | --- | --- | --- | | 3.1.1 | 只有启用了 MFA 的身份才能访问特权虚拟机 | L2 | **手动** —— 需要将角色分配与 MFA 状态相关联 | ### 第 5 部分 —— 身份服务(8 项自动化 · 7 项手动) | 控制项 | 标题 | 级别 | 注意 | | --- | --- | --- | --- | | 5.1.1 | 在 Microsoft Entra ID 中启用了“安全默认值” | L1 | | | 5.1.2 | 注册或加入设备需要 MFA | L1 | **手动** | | 5.1.3 | 为所有用户启用了“多重身份验证” | L1 | | | 5.1.4 | 在受信任的设备上禁用“记住 MFA” | L1 | **手动** | | 5.3.1 | Azure 管理员账户不用于日常操作 | L1 | **手动** | | 5.3.2 | 定期审查来宾用户 | L1 | **手动** —— 当不存在来宾时通过;通过 Graph 提取来宾清单 | | 5.3.3 | 限制使用“User Access Administrator”角色 | L1 | | | 5.3.4 | 定期审查特权角色分配 | L1 | **手动** | | 5.3.5 | 已禁用的账户没有 Read/Write/Owner 权限 | L1 | | | 5.3.6 | 定期审查“Tenant Creator”角色分配 | L1 | **手动** | | 5.3.7 | 定期审查非特权角色分配 | L1 | **手动** | | 5.4 | 没有自定义订阅管理员角色 | L1 | | | 5.5 | 分配了用于管理资源锁的自定义角色 | L2 | | | 5.6 | 订阅离开/进入租户设置为“Permit no one” | L2 | | | 5.7 | 订阅所有者数量在 2 到 3 之间 | L1 | | ### 第 6 部分 —— 日志记录与监控(15 项自动化 · 9 项手动) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 6.1.1.1 | 为订阅活动日志提供了诊断设置 | L1 | | 6.1.1.2 | 诊断设置捕获了相应的类别 | L1 | | 6.1.1.3 | 包含 activity-logs 容器的存储账户使用 CMK 加密(手动) | L2 | | 6.1.1.4 | 启用了 Azure Key Vault 的日志记录 | L1 | | 6.1.1.5 | NSG 流日志被捕获并发送到 Log Analytics(手动) | L2 | | 6.1.1.6 | 虚拟网络流日志被捕获并发送到 Log Analytics(手动) | L2 | | 6.1.1.7 | 用于 Microsoft Graph 活动日志的 Entra 诊断设置(手动) | L2 | | 6.1.1.8 | 用于 Microsoft Entra 活动日志的 Entra 诊断设置(手动) | L2 | | 6.1.1.9 | Intune 日志被捕获并发送到 Log Analytics(手动) | L2 | | 6.1.2.1 | 活动日志警报:创建策略分配 | L1 | | 6.1.2.2 | 活动日志警报:删除策略分配 | L1 | | 6.1.2.3 | 活动日志警报:创建或更新 NSG | L1 | | 6.1.2.4 | 活动日志警报:删除 NSG | L1 | | 6.1.2.5 | 活动日志警报:创建或更新安全解决方案 | L1 | | 6.1.2.6 | 活动日志警报:删除安全解决方案 | L1 | | 6.1.2.7 | 活动日志警报:创建或更新 SQL 防火墙规则 | L1 | | 6.1.2.8 | 活动日志警报:删除 SQL 防火墙规则 | L1 | | 6.1.2.9 | 活动日志警报:创建或更新公共 IP | L1 | | 6.1.2.10 | 活动日志警报:删除公共 IP | L1 | | 6.1.2.11 | 活动日志警报:服务运行状况 | L1 | | 6.1.3.1 | 配置了 Application Insights | L2 | | 6.1.4 | 为所有受支持的服务启用了 Azure Monitor 资源日志记录(手动) | L1 | | 6.1.5 | 生产工件不使用 Basic/Free/Consumption SKU(手动) | L2 | | 6.2 | 为关键任务资源设置了资源锁(手动) | L2 | ### 第 7 部分 —— 网络服务(13 项自动化 · 3 项手动) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 7.1 | RDP (3389) 未对 Internet 开放 | L1 | | 7.2 | SSH (22) 未对 Internet 开放 | L1 | | 7.3 | 限制了来自 Internet 的 UDP 端口访问 | L1 | | 7.4 | 评估并限制了来自 Internet 的 HTTP/HTTPS (80/443) | L1 | | 7.5 | NSG 流日志保留期 >= 90 天 | L2 | | 7.6 | 为所有正在使用的区域启用了网络观察程序 | L2 | | 7.7 | 定期评估公共 IP 地址(手动) | L1 | | 7.8 | VNet 流日志保留期 >= 90 天 | L2 | | 7.9 | VPN Gateway P2S 身份验证类型 = 仅限 Entra ID(手动) | L2 | | 7.10 | 在 Azure Application Gateway 上启用了 WAF | L2 | | 7.11 | 子网与 NSG 关联 | L1 | | 7.12 | 应用网关 SSL 策略最低 TLS 1.2+ | L1 | | 7.13 | 在 Application Gateway 上启用了 HTTP2 | L1 | | 7.14 | 启用了 WAF 请求正文检查 | L2 | | 7.15 | 启用了 WAF 机器人防护 | L2 | | 7.16 | 为 PaaS 资源使用了网络安全边界(手动) | L2 | ### 第 8 部分 —— 安全服务(30 项自动化 · 8 项手动) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 8.1.1.1 | Microsoft Defender CSPM | L2 | | 8.1.2.1 | Microsoft Defender for APIs | L2 | | 8.1.31 | Microsoft Defender for Servers | L2 | | 8.1.3.2 | 计算机组件的漏洞评估(手动) | L2 | | 8.1.3.3 | 终结点保护 (WDATP) 组件 | L2 | | 8.1.3.4 | 计算机的无代理扫描组件(手动) | L2 | | 8.1.3.5 | 文件完整性监控组件(手动) | L2 | | 8.1.4.1 | Microsoft Defender for Containers | L2 | | 8.1.5.1 | Microsoft Defender for Storage | L2 | | 8.1.5.2 | 监控存储账户的 ATP 警报(手动) | L2 | | 8.1.6.1 | Microsoft Defender for App Services | L2 | | 8.1.7.1 | Microsoft Defender for Azure Cosmos DB | L2 | | 8.1.7.2 | Microsoft Defender for Open-Source Relational DBs | L2 | | 8.1.7.3 | Microsoft Defender for SQL (托管实例) | L2 | | 8.1.7.4 | Microsoft Defender for SQL Servers on Machines | L2 | | 8.1.8.1 | Microsoft Defender for Key Vault | L2 | | 8.1.9.1 | Microsoft Defender for Resource Manager | L2 | | 8.1.10 | Defender 配置为检查 VM 操作系统更新 | L1 | | 8.1.11 | 未禁用非废弃的 MCSB 策略(手动) | L1 | | 8.1.12 | 安全警报通知订阅所有者 | L1 | | 8.1.13 | 安全联系人的其他电子邮件地址 | L1 | | 8.1.14 | 配置了警报严重性通知 | L1 | | 8.1.15 | 配置了攻击路径通知 | L1 | | 8.1.16 | Defender 外部攻击面监控 (EASM)(手动) | L2 | | 8.2.1 | Microsoft Defender for IoT Hub(手动) | L2 | | 8.3.1 | 设置了密钥过期时间 —— 使用 RBAC 的 Key Vault | L1 | | 8.3.2 | 设置了密钥过期时间 —— 访问策略(遗留) | L1 | | 8.3.3 | 设置了机密过期时间 —— 使用 RBAC 的 Key Vault | L1 | | 8.3.4 | 设置了机密过期时间 —— 访问策略(遗留) | L1 | | 8.3.5 | 启用了 Key Vault 清除保护 | L1 | | 8.3.6 | 启用了 Key Vault RBAC 授权 | L2 | | 8.3.7 | 禁用了 Key Vault 公共网络访问 | L1 | | 8.3.8 | 使用专用终结点访问 Key Vault | L2 | | 8.3.9 | 启用了自动密钥轮换 | L2 | | 8.3.10 | 需要时使用 Azure Key Vault 托管 HSM(手动) | L2 | | 8.3.11 | 证书有效期 <= 12 个月 | L1 | | 8.4.1 | 存在 Azure Bastion Host | L2 | | 8.5 | 在 VNet 上启用了 DDoS 网络防护 | L2 | ### 第 9 部分 —— 存储服务(21 项自动化) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 9.1.1 | 启用了 Azure 文件共享的软删除 | L1 | | 9.1.2 | SMB 协议版本 >= 3.1.1 | L1 | | 9.1.3 | SMB 通道加密为 AES-256-GCM 或更高 | L1 | | 9.2.1 | 启用了 Blob 软删除 | L1 | | 9.2.2 | 启用了容器软删除 | L1 | | 9.2.3 | 启用了 Blob 版本控制 | L2 | | 9.3.1.1 | 启用了密钥轮换提醒 | L1 | | 9.3.1.2 | 访问密钥在 90 天内重新生成 | L1 | | 9.3.1.3 | 禁用了存储账户密钥访问 | L1 | | 9.3.2.1 | 使用专用终结点访问存储账户 | L2 | | 9.3.2.2 | 禁用了公共网络访问 | L1 | | 9.3.2.3 | 默认网络访问规则为拒绝 | L1 | | 9.3.3.1 | 在 Azure 门户中默认使用 Microsoft Entra 授权 | L1 | | 9.3.4 | 需要安全传输 (HTTPS) | L1 | | 9.3.5 | 允许受信任的 Microsoft 服务访问存储 | L2 | | 9.3.6 | 最低 TLS 版本 1.2 | L1 | | 9.3.7 | 禁用了跨租户复制 | L1 | | 9.3.8 | 禁用了 Blob 匿名访问 | L1 | | 9.3.9 | 存储账户应用了 ARM 删除锁 | L1 | | 9.3.10 | 考虑为存储账户应用 ARM ReadOnly 锁 | L2 | | 9.3.11 | 冗余设置为异地冗余 (GRS) | L2 | ## 测试 测试套件使用 **Pester 5** —— 所有测试均模拟 Az 模块调用,因此不需要真实的 Azure 连接。 ### 运行所有测试 ``` # 需要 Pester 5.0+ Install-Module Pester -Force -Scope CurrentUser # 运行所有测试 .\Tests\Run-Tests.ps1 ``` ### 运行单个检查 ``` .\Tests\Run-Tests.ps1 -Test "5_27" ``` ### 包含代码覆盖率 ``` .\Tests\Run-Tests.ps1 -Coverage # 当覆盖率低于下限时失败(退出代码为 1) — 这正是 CI 所强制执行的 .\Tests\Run-Tests.ps1 -Coverage -MinCoverage 80 ``` 覆盖率测量 `Private/*.ps1` 和 `Checks/*.ps1` 并写入 `coverage.xml` (JaCoCo)。在 GitHub Actions 中,该百分比也会发布到作业摘要中。 ### 持续集成 GitHub Actions pipeline 会在每次推送和拉取请求时运行: - Pester 测试(Ubuntu + Windows) - 代码覆盖率底线为 80%(Ubuntu 环境,发布在作业摘要中) - PSScriptAnalyzer lint 检查 工作流文件位于 `.github/workflows/ci.yml`。 #### 在您自己的 CI/CD pipeline 中使用此工具 添加 `-ExitCode`,以便在发现任何 FAIL 或 ERROR 结果时使工具以代码 2 退出。 这使得 Azure DevOps、GitHub Actions 和类似系统能够在出现合规性倒退时使构建失败: ``` # GitHub Actions 示例 - name: Azure foundations audit shell: pwsh run: | .\Invoke-CISAzureAudit.ps1 ` -Subscriptions "Production" ` -NoOpen ` -ExitCode # Step fails (exit code 2) if any controls are non-compliant ``` ``` # Azure DevOps 示例 - task: PowerShell@2 displayName: 'Azure Foundations audit' inputs: targetType: inline pwsh: true script: | .\Invoke-CISAzureAudit.ps1 -Subscriptions $(SUB_NAME) -NoOpen -ExitCode # Marks the pipeline stage as failed when FAIL/ERROR results are found ``` #### 将发现上传到 GitHub 代码扫描 每次运行还会在报告旁边写入一个 SARIF 2.1.0 日志。将其上传以在代码库的 **Security → Code scanning**(代码扫描)选项卡中显示发现(私有代码库需要 GitHub Advanced Security;公共代码库免费): ``` - name: Azure foundations audit shell: pwsh run: | .\Invoke-CISAzureAudit.ps1 ` -Subscriptions "Production" ` -Output reports/cis.html ` -NoOpen # No -ExitCode: let the upload step run even when there are findings - name: Upload SARIF to code scanning if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: reports/cis.sarif category: cis-azure-benchmark ``` 警报由控制项 + 订阅 + 资源作为键值,因此在多次运行中持续存在的发现将保留为单个警报,而消失的发现将被自动关闭。 #### 使用 OIDC 计划审计(无需存储 secret) 对于定期审计,请使用 GitHub 的 OIDC 联合身份验证:工作流在运行时将短期的 GitHub token 换取为 Azure token,因此代码库中永远不会存储任何 client secret 或证书。 一次性 Azure 设置 —— 带有联合凭据的应用注册: ``` # 1. App registration + service principal az ad app create --display-name "cis-azure-audit" # note the appId in the output az ad sp create --id # 2. 为此 repository 的 default branch 信任 GitHub 的 OIDC issuer # (scheduled workflows 始终在 default branch 上运行,因此 subject 必须与之匹配) az ad app federated-credential create --id --parameters '{ "name": "github-scheduled-audit", "issuer": "https://token.actions.githubusercontent.com", "subject": "repo:/:ref:refs/heads/main", "audiences": ["api://AzureADTokenExchange"] }' # 3. 为每个要审计的 subscription 授予读取权限 az role assignment create --assignee --role "Reader" --scope /subscriptions/ az role assignment create --assignee --role "Security Reader" --scope /subscriptions/ ``` 对于身份检查(第 5.x 部分,Microsoft Graph),还需要为 service principal 分配 **Global Reader** Entra 角色;如果没有它,这些控制项将返回 ERROR 并指出缺少的权限 —— 此时合规性未知,而不是假定为合规。 将 `AZURE_CLIENT_ID`(即 appId)、`AZURE_TENANT_ID` 和 `AZURE_SUBSCRIPTION_ID` 作为代码库的**变量**存储(Settings → Secrets and variables → Actions → Variables)—— 它们都不是 secret。 然后是工作流: ``` name: Weekly CIS Azure audit on: schedule: - cron: '17 6 * * 1' # Mondays 06:17 UTC workflow_dispatch: {} # allow manual runs too permissions: id-token: write # required for the OIDC token exchange contents: read jobs: audit: runs-on: ubuntu-latest steps: - name: Install the audit module shell: pwsh run: | # Pulls the Az module dependencies automatically. Pin -Version for # reproducible scheduled runs if you prefer. Install-PSResource CISAzureFoundationsBenchmark -TrustRepository -Scope CurrentUser - name: Azure login (OIDC) uses: azure/login@v2 with: client-id: ${{ vars.AZURE_CLIENT_ID }} tenant-id: ${{ vars.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} enable-AzPSSession: true # the tool uses Az PowerShell, not az CLI - name: Run audit shell: pwsh run: | Invoke-CISAzureAudit ` -Subscriptions "${{ vars.AZURE_SUBSCRIPTION_ID }}" ` -Output reports/cis.html ` -NoOpen ` -ExitCode - name: Upload report artifacts if: always() # keep the reports even when -ExitCode fails the job uses: actions/upload-artifact@v4 with: name: cis-audit-report path: reports/ ``` 构件包含所有四个报告文件(`cis.html`、`cis.json`、`cis.csv`、`cis.sarif`)。若还要在 Security 选项卡中显示发现,请追加上一节中的 `upload-sarif` 步骤(将 `security-events: write` 添加到工作流的 `permissions` 块中)。 退出代码摘要: | 代码 | 含义 | | --- | --- | | `0` | 审计完成 —— 所有控制项均通过(或所有失败项均已被抑制) | | `1` | 工具设置错误 —— 缺少 Az PowerShell 模块、未登录 Azure 或未找到可访问的订阅 | | `2` | 合规性失败 —— 检测到一个或多个 FAIL 或 ERROR 结果(仅在指定了 `-ExitCode` 时发生) | ## 检查点文件 ``` cis_checkpoints/ |- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json <- completed |- yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy.json <- completed `- zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz.json <- failed (retried on next run) ``` 每个文件包含该订阅的完整结果集、UTC 时间戳和完成状态。删除 `cis_checkpoints/` 文件夹或使用 `-Fresh` 可放弃所有检查点。使用 `-ReportOnly` 可在现有检查点的基础上重新生成 HTML 报告,而无需运行任何检查。 ## 已知限制 **只读** —— 该脚本仅进行审计。它不会对您的环境进行任何更改。 **时间点快照** —— 结果反映的是脚本运行那一刻的状态。 **Key Vault 数据平面访问** —— 列出密钥、机密和证书除了需要订阅 Reader 权限外,还需要数据平面权限。请分配 **Key Vault Reader**(RBAC 保管库)或将运行者账户添加到保管库的访问策略中(非 RBAC 保管库)。如果没有此权限,受影响的检查将返回 ERROR 并附带说明消息。报告会清楚地将此与合规结果区分开 —— 此时合规性未知,而不是假定为合规。 **用于身份检查的 Graph API** —— 控制项 5.1.1、5.1.3、5.3.2 和 5.3.5 通过 `Invoke-AzRestMethod` 调用 Microsoft Graph API。如果尚未为运行审计的身份同意所需的 Graph 权限,这些检查将返回 ERROR。控制项 5.6 通过 ARM 读取租户级别的订阅策略,当调用者缺少租户范围的读取访问权限时返回 ERROR。 请使用以下命令进行测试: ``` Invoke-AzRestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/policies/identitySecurityDefaultsEnforcementPolicy" ``` ## 故障排除 **身份检查返回 ERROR (AccessDenied)** 您的账户需要在 Entra ID 中具有 Global Reader 权限。要验证 Graph API 访问权限: ``` Invoke-AzRestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" ``` 如果返回 403 错误,请要求您的 Entra ID 管理员授予 Global Reader 权限,或同意用于审计的 service principal 所需的 Graph API 权限。 **Key Vault 检查返回 ERROR(审计未完成)** 运行者账户需要 Key Vault 数据平面访问权限。对于启用了 RBAC 的保管库,请分配 **Key Vault Reader** 角色;对于访问策略保管库,请将该账户添加到保管库的访问策略中。报告中的 ERROR 消息会准确指出缺少的内容。 **找不到订阅** 传递给 `-Subscriptions` 的值必须与现有的订阅名称或 ID 完全匹配。 运行 `Get-AzSubscription | Select-Object Name, Id` 以查看可用的订阅。 **运行中断** 重新运行相同的命令以从中断处恢复,或添加 `-Fresh` 以重新开始。 ## 免责声明 ## 鸣谢 ## 许可证 [MIT](LICENSE) **版本:** 2.4.0 **Benchmark:** CIS Microsoft Azure Foundations Benchmark v6.0.0(2026年4月) **覆盖范围:** 涵盖 7 个部分的 93 项自动化控制 · 输出中注明的 34 项手动控制(共 127 项)
标签:AI合规, Anthropic, CIS基准, IPv6, Libemu, Microsoft Azure, PowerShell, 云安全审计, 关系图谱, 基线检查, 安全合规, 网络代理