vegazbabz/CIS-Azure-Benchmark-Audit-Tool
GitHub: vegazbabz/CIS-Azure-Benchmark-Audit-Tool
基于 CIS Azure 基准的轻量级自动化合规审计工具,无需外部依赖即可生成带修复指引的交互式 HTML 报告。
Stars: 3 | Forks: 0
# CIS Microsoft Azure Foundations Benchmark v5.0.0 — 审计工具
[](LICENSE)
[](https://www.cisecurity.org/benchmark/azure)
[](https://www.python.org/)
[](https://github.com/vegazbabz/CIS-Azure-Benchmark-Audit-Tool/actions/workflows/ci.yml)

**版本:** 1.0.0-beta3
**基准:** [CIS Microsoft Azure Foundations Benchmark v5.0.0](https://www.cisecurity.org/benchmark/azure) (2025年9月)
**覆盖范围:** 94项自动化控制 · 输出中注明了2项手动控制 · 1项控制待定 (2.1.1)
## 概述
一个针对 **[CIS Microsoft Azure Foundations Benchmark v5.0.0](https://www.cisecurity.org/benchmark/azure)** 对 Azure 租户进行审计的 Python 工具 —— 这是由 [Center for Internet Security (CIS)](https://www.cisecurity.org/) 发布的 Azure 环境行业标准加固指南。
除标准库外,它不需要任何 pip 安装 —— 仅需 Python 3.10+ 和 Azure CLI。
结果会在每个订阅完成后作为检查点保存,因此失败或中断的运行
可以在不重新运行已完成工作的情况下恢复。输出是一个包含
过滤、合规性评分、图表和针对每项发现的修复指南的独立 HTML 报告。JSON 和 CSV 导出文件
会随 HTML 自动生成。
## 系统要求
### 运行时
| 要求 | 详情 |
| --- | --- |
| Python | [3.10 或更高版本](https://www.python.org/downloads/) |
| Azure CLI | 任何最新版本 — |
| resource-graph 扩展 | 首次运行时自动安装 |
| Azure 登录 | 运行前完成 `az login` |
| msal | `pip install -r requirements.txt` — 检查项 5.1.1 必需(可选,见下文) |
### Azure 权限
| 范围 | 角色 | 用途 |
| --- | --- | --- |
| 每个订阅 | Reader | 枚举所有资源 |
| 每个订阅 | Security Reader | Defender 计划,安全联系人 |
| Microsoft Entra ID (租户) | Global Reader | 身份检查 (5.x) |
| Key Vaults (可选) | Key Vault Reader | 列出 8.3.x 检查的密钥、机密、证书 |
### 依赖项
该工具有一个运行时依赖项 —— `msal` —— 仅用于检查项 5.1.1 (安全默认值)。
使用以下命令安装:
```
pip install -r requirements.txt
```
如果未安装 `msal`,检查项 5.1.1 仍将通过 `az` CLI 路径运行,但将返回
ERROR,因为 `az` CLI 应用无法获取 `Policy.Read.All`。所有其他检查没有 pip
依赖项。
开发依赖项(`black`, `flake8`, `mypy`, 以及可选的 `rich`)列在 `requirements-dev.txt` 中:
```
pip install -r requirements-dev.txt
```
## 快速开始
```
# 登录 Azure
az login
# 运行审计(审计所有已启用的 subscriptions)
python cis_azure_audit.py
```
审计完成后,报告将自动在您的浏览器中打开。该脚本还将
在缺失时自动安装 `resource-graph` 扩展,枚举所有已启用的
订阅,运行所有检查,并将报告文件保存到 `reports/` 子目录。
## 入门指南(分步说明)
不熟悉 Python 或 Azure CLI?按照以下步骤在您的机器上运行该工具。
### 步骤 1 — 安装 Python
1. 前往 [https://www.python.org/downloads/](https://www.python.org/downloads/) 并下载
适用于您操作系统的最新 **Python 3.10+** 安装程序。
2. 运行安装程序。在 Windows 上,在点击 Install 之前勾选 **"Add Python to PATH"**。
3. 验证安装:
python --version
### 步骤 2 — 安装 Azure CLI
1. 按照 上适用于您操作系统的官方说明进行操作。
2. 验证:
az --version
### 步骤 3 — 获取工具
**选项 A — 使用 Git 克隆**(推荐 —— 便于更新):
```
git clone https://github.com/vegazbabz/CIS-Azure-Benchmark-Audit-Tool.git
cd CIS-Azure-Benchmark-Audit-Tool
```
**选项 B — 下载 ZIP**(无需 Git):
1. 在 [GitHub 仓库页面](https://github.com/vegazbabz/CIS-Azure-Benchmark-Audit-Tool),
点击绿色的 **Code** 按钮 → **Download ZIP**。
2. 解压 ZIP 并在解压后的文件夹中打开终端。
### 步骤 4 — 安装 Python 依赖项
唯一的运行时依赖是 `msal`(用于一项可选检查)。使用以下命令安装:
```
pip install -r requirements.txt
```
### 步骤 5 — 登录 Azure
```
az login
```
将打开一个浏览器窗口供您登录。您使用的帐户至少需要对
您要审计的订阅拥有 **Reader** 和 **Security Reader** 权限。
### 步骤 6 — 运行审计
```
python cis_azure_audit.py
```
该工具将枚举您所有已启用的订阅,运行所有检查,并在完成后
自动在您的浏览器中打开 HTML 报告。报告将保存到 `reports/` 子文件夹。
## 项目结构
```
cis_azure_audit.py Main entry point and CLI
cis_audit.toml Optional configuration file (parallel, timeouts, etc.)
requirements.txt Runtime pip dependency (msal)
azure/
client.py az CLI wrappers, retry logic, error classification helpers
graph_auth.py MSAL-based Graph auth for Policy.Read.All (check 5.1.1)
helpers.py Shared Azure utilities
identity.py Permission preflight and role helpers
checks/
s2.py Section 2 — Databricks
s5.py Section 5 — Identity
s6.py Section 6 — Monitoring
s7.py Section 7 — Networking
s8.py Section 8 — Security Services
s9.py Section 9 — Storage
cis/
checkpoint.py Checkpoint read/write, result reclassification on load
check_helpers.py Shared check utilities (port ranges, NSG rules, etc.)
config.py Config file loader (cis_audit.toml)
helpers.py Logging setup, console output
history.py Run history for compliance trend tracking
models.py Result dataclass (R)
report.py HTML report generation, JSON/CSV export
suppressions.py Finding suppression (accepted risks)
scripts/
preflight_check.py Standalone permission check script
tests/ Unit test suite (no Azure connection required)
```
## 配置文件
`cis_audit.toml` 从 `cis_azure_audit.py` 所在的目录自动加载。
所有设置都是可选的 —— 省略任何行以保留内置默认值。
可以使用 `CIS_AUDIT_CONFIG` 环境变量覆盖该路径。
```
[audit]
parallel = 3 # Concurrent subscription workers (1–20)
executor = "thread" # "thread" (recommended on Windows) or "process"
checkpoint_dir = "cis_checkpoints"
[timeouts]
default = 20 # Most az CLI calls (seconds)
storage_list = 30 # az storage account list
storage_svc = 15 # Per-account blob/file/table service queries
activity_log = 25 # Activity log queries
graph = 120 # Resource Graph bulk queries
# 可选 — 启用 5.1.1 (Security defaults) 的自动化评估。
# 请参阅下方的“Check 5.1.1 setup”了解一次性的 app registration 步骤。
[graph_auth]
# client_id = "00000000-0000-0000-0000-000000000000"
# tenant_id = "" # 可选 — 通过 az account show 自动检测
# client_secret = "" # 仅限 SP / CI;交互式用户认证请省略
```
当 CLI 标志和 `cis_audit.toml` 值同时设置时,前者优先。
## 使用方法
```
python cis_azure_audit.py [options]
```
### 所有选项
| 选项 | 描述 |
| --- | --- |
| `-s`, `--subscription` | 按名称或 GUID 审计一个或多个订阅。多个值跟在单个标志后:`-s Sub1 Sub2 Sub3` |
| `-o`, `--output` | 输出 HTML 文件名(默认:`reports/cis_azure_audit_report_.html`) |
| `--output-dir` | 所有输出文件的目录(HTML, JSON, CSV, 检查点) |
| `-p`, `--parallel` | 并发订阅工作线程数(默认:来自配置或 3) |
| `--executor` | 工作线程后端:`thread`(默认)或 `process` |
| `--no-adaptive-concurrency` | 检测到限制时禁用动态工作线程调整 |
| `-l`, `--level` | 仅过滤输出为 Level `1` 或 `2` 的控制项 |
| `--fresh` | 清除所有检查点并开始完全重新审计 |
| `--report-only` | 从现有检查点重新生成 HTML/JSON/CSV —— 无 API 调用 |
| `--suppressions` | 屏蔽规则 TOML 文件的路径(默认:脚本旁的 `suppressions.toml`) |
| `--list-suppressions` | 打印所有活动的屏蔽规则并退出 |
| `--no-open` | 审计完成后不要在浏览器中自动打开报告 |
| `--skip-preflight` | 跳过权限预检 |
| `-q`, `--quiet` | 抑制每项检查的进度行;仅显示摘要 |
| `--log-level` | 基础日志级别:`TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
| `-v`, `--verbose` | 详细日志记录(设置 `DEBUG`) |
| `--debug` | 跟踪日志记录(设置 `TRACE`) |
| `--log-file` | 将完整日志写入文件,除了控制台外 |
### 示例
```
# 审计所有 subscriptions
python cis_azure_audit.py
# 按名称审计单个 subscription
python cis_azure_audit.py -s "Production"
# 审计多个 subscriptions
python cis_azure_audit.py -s "Production" "Staging"
# 使用更多 parallel workers 运行得更快
python cis_azure_audit.py --parallel 5
# 使用 thread workers(Windows 上推荐)
python cis_azure_audit.py --executor thread --parallel 4
# 将所有内容保存到特定文件夹
python cis_azure_audit.py --output-dir C:\AuditResults
# 运行中断?只需重新运行 — 它会自动恢复
python cis_azure_audit.py
# 完全从头开始,忽略之前的 checkpoints
python cis_azure_audit.py --fresh
# 重新生成 HTML report,无需重新运行任何 checks
python cis_azure_audit.py --report-only
# 仅限 Level 1 controls
python cis_azure_audit.py --level 1
# 安静模式 — 隐藏每个 check 的输出行,仅显示摘要
python cis_azure_audit.py --quiet
# 写入 log file 的 Trace 级别诊断信息
python cis_azure_audit.py --debug --log-file cis_audit.log
# 查看当前被屏蔽的 findings
python cis_azure_audit.py --list-suppressions
# 编辑 suppressions.toml 后重新生成 report(无需重新审计)
python cis_azure_audit.py --report-only
```
### 并发调优
当检测到 Azure API 限制 (HTTP 429) 时,审计会自动调整工作线程数:
- 从 `--parallel` 个工作线程开始(最少 1 个)。
- 当暂时性限制重试激增时减少工作线程。
- 在批次稳定后逐渐恢复工作线程。
使用 `--no-adaptive-concurrency` 保持工作线程数固定。
#### 对您自己的默认值进行基准测试
```
$py = "python"
$sub = ""
$runs = @(
@{executor="thread"; parallel=2},
@{executor="thread"; parallel=4},
@{executor="process"; parallel=2}
)
foreach ($r in $runs) {
$label = "$($r.executor)-p$($r.parallel)"
$log = "bench_$label.log"
$elapsed = Measure-Command {
& $py cis_azure_audit.py --subscription "$sub" --executor $r.executor --parallel $r.parallel `
--level 1 --fresh --skip-preflight --output "bench_$label.html" --log-file $log *> $null
}
$retries = (Select-String -Path $log -Pattern "transient error" -SimpleMatch | Measure-Object).Count
Write-Output "$label : $([Math]::Round($elapsed.TotalSeconds,2))s retries=$retries"
}
```
每个候选方案运行 2-3 次并比较中位运行时间,而不是单次运行。
## 工作原理
### 数据收集 —— 三种方法
#### 1. Azure Resource Graph(批量预取 —— 每次审计一次)
在任何针对每个订阅的工作开始之前,Kusto 查询会在
单次往返中获取整个租户的所有相关资源:
- 网络安全组和安全规则
- 存储帐户和安全属性
- Key Vault —— 访问配置和网络设置
- 虚拟网络、子网和 NSG 关联
- 应用程序网关和 WAF 设置
- Databricks 工作区
- Bastion Host
- Network Watcher 和资源位置
- 角色分配(所有者和用户访问管理员)
- WAF 策略
#### 2. 每个订阅的 Azure CLI 调用
用于 Resource Graph 无法公开的实时服务配置和数据:
- `az security pricing show` — Defender 计划状态 (8.1.x)
- `az security contact list` — 通知设置 (8.1.12–8.1.15)
- `az monitor diagnostic-settings list` — Key Vault 和 App Service 日志记录
- `az monitor activity-log alert list` — 所有 11 项警报检查 (6.1.2.x)
- `az keyvault key/secret list` — 每个密钥和机密的到期日期
- `az keyvault key rotation-policy show` — 自动轮换配置
- `az keyvault certificate show` — 证书有效期
- `az storage account blob-service-properties show` — 软删除、版本控制
- `az storage account file-service-properties show` — 文件软删除、SMB 设置
- `az network watcher flow-log list` — 流日志保留 (7.5, 7.8)
- `az role definition list` — 自定义管理员角色 (5.23)
#### 3. 通过 `az rest` 的 Azure REST API
用于无法通过 az CLI 获得的租户级身份检查:
- `graph.microsoft.com/v1.0/policies/authorizationPolicy` — 涵盖 5.4, 5.14, 5.15, 5.16
- ARM REST 用于 WDATP 集成设置 (8.1.3.3) 和攻击路径通知 (8.1.15)
### 权限预检
在审计开始之前,该工具会检查运行帐户是否在每个
订阅上拥有所需角色。在大型租户上,这会并行运行(一次最多 8 个订阅),因此
检查在几秒钟而不是几分钟内完成。
### 检查点和恢复
在每个订阅完成后,结果将写入 `cis_checkpoints/.json`。
如果脚本在运行中途停止或崩溃,重新运行它将跳过已完成的订阅并
从中断处继续。使用 `--fresh` 丢弃所有检查点并重新开始。
按一次 **Ctrl+C** 即可干净退出:进行中的 Azure CLI 子进程立即被终止,
打印一条消息告诉您如何恢复或重新开始,并且进程退出而不会
留下孤立的后台进程或冻结的终端。
### 并行执行
订阅通过 Python 的 `concurrent.futures` 执行器并发运行。默认为 3 个并行
工作线程(可在 `cis_audit.toml` 中配置或通过 `--parallel`)。Resource Graph 预取始终
在并行循环开始前运行一次。
## HTML 报告
生成的报告是一个没有外部依赖项的独立 HTML 文件。
- **摘要卡片** — 合规性评分(PASS / 总数,不包括 INFO 和 MANUAL),以及每种状态的计数。
- **合规性圆环图** — 三个环形图,分别显示整体、Level 1 和 Level 2 的 PASS/FAIL/ERROR 比例。
- **部分细分** — 每个 CIS 部分的水平堆叠条,从最差到最好排序。
- **每个订阅的摘要** — 显示每个订阅的通过/失败/错误计数的堆叠条表格;单击一行以将结果表过滤到该订阅。
- **可过滤表格** — 同时按自由文本搜索、订阅、状态和级别 (L1/L2) 过滤。当所有结果都被过滤掉时,部分标题会折叠。
- **每个资源的结果** — 每个 NSG、存储帐户、Key Vault、子网和 Databricks 工作区都单独报告,而不是聚合为每个控制项的单个通过/失败。
- **修复提示** — 每个 FAIL 结果都包含用于解决问题的 Azure 门户导航路径。ERROR 结果包含关于缺少什么访问权限的可操作说明。
- **导出** — JSON 和 CSV 文件在报告时随 HTML 自动生成。在报告中单击 **Export JSON** 或 **Export CSV** 下载它们。
- **合规性趋势** — 在两次或更多次完整租户审计运行后,结果表上方会出现一个可折叠图表,显示随时间变化的合规性评分。默认折叠,以免分散对当前发现的注意力。使用 `--subscription` 过滤器运行时不显示,因为过滤后的运行反映的是租户的一个子集,无法与完整租户基线进行比较。
- **回到顶部** — 用于长报告的右下角固定按钮。
### 状态类型
| 状态 | 含义 |
| --- | --- |
| PASS | 控制项合规 |
| FAIL | 控制项不合规 —— 提供修复提示 |
| ERROR | 检查无法完成 —— 审计缺口(权限缺失、超时或 API 错误)。合规性**未知**;请勿视为正常。 |
| INFO | 不适用 —— 资源类型不存在或帐户类型不支持该功能(例如 ADLS Gen2 没有 blob/file 服务) |
| MANUAL | 无法自动化 —— 需要根据 CIS PDF 进行手动验证 |
| SUPPRESSED | 已接受风险 —— 在 `suppressions.toml` 中定义,带有理由和到期日 |
### `--report-only`:重新生成报告而不重新审计
```
python cis_azure_audit.py --report-only
```
加载所有现有检查点并重新生成 HTML、JSON 和 CSV —— 不进行任何 Azure API 调用。
在以下情况后很有用:
- 编辑 `suppressions.toml` 以接受一项发现
- 升级工具(加载时应用新的错误分类和消息格式)
- 更改 `--level` 过滤器
第 5 部分租户级身份检查(5.1.1, 5.1.2, 5.1.3, 5.4, 5.14, 5.15, 5.16)在
报告时**重新运行**,因为它们不存储在订阅检查点中。实时 Graph API
结果(5.4, 5.14, 5.15, 5.16)将反映您租户的当前状态。MANUAL 发现
(5.1.3)将始终出现。如果 Graph 无法访问,这些检查将被跳过并发出警告,并且
报告仍从检查点生成。
从检查点加载的结果会使用当前工具逻辑自动重新分类:
旧检查点中记录为 ERROR 的 `FeatureNotSupportedForAccount` 存储错误显示为
INFO,Key Vault 权限错误被重写为清晰的可操作消息,并且工具更新版本中引入的
任何部分名称更正都会在加载时应用。
### 输出文件
每次运行生成三个报告文件并更新运行历史记录:
| 文件 | 内容 |
| --- | --- |
| `reports/cis_azure_audit_report_.html` | 独立交互式报告(在浏览器中自动打开) |
| `reports/cis_azure_audit_report_.json` | 作为 JSON 数组的所有结果 |
| `reports/cis_azure_audit_report_.csv` | 作为扁平 CSV 的所有结果 |
| `reports/cis_run_history.json` | 趋势图的合规性评分历史(最近 30 次完整租户运行) |
使用 `--output` 设置自定义文件名,或使用 `--output-dir` 将所有输出重定向到不同的目录。
历史文件始终写入与 HTML 报告相同的目录.
## 屏蔽发现(已接受风险)
有些发现是有意的 —— 例如,故意开放 RDP 的跳转主机,或
特定环境不需要的 DDoS 计划。如果没有确认这些的方法,
每个报告都包含相同的已知发现,审查者就会不再信任输出。
`cis_azure_audit.py` 旁边的 `suppressions.toml` 文件允许您将特定发现标记为
已接受风险。被屏蔽的发现仍以灰色显示在报告中,并显示您的理由
—— 它们永远不会被隐藏。合规性评分将其排除(像 INFO 和 MANUAL 一样)。
屏蔽规则**仅在报告生成时**应用。检查点始终存储原始
FAIL,因此删除屏蔽规则并运行 `--report-only` 会立即恢复该发现。
### suppressions.toml 格式
```
[[suppressions]]
control_id = "7.1"
resource = "jumphost-nsg" # optional — exact match; omit to match all resources
subscription = "Production" # optional — exact match; omit to match all subscriptions
justification = "Intentional RDP jump host — restricted by Azure Firewall IP allowlist"
expires = "2026-12-31" # required — ISO date, maximum 1 year from today
[[suppressions]]
control_id = "8.5"
justification = "DDoS protection not required — no public-facing workloads in this tenant"
expires = "2026-06-01"
```
### 屏蔽规则
| 字段 | 必需 | 行为 |
| --- | --- | --- |
| `control_id` | 是 | 与 CIS 控制编号完全匹配 |
| `resource` | 否 | 与资源名称不区分大小写完全匹配;省略以匹配所有 |
| `subscription` | 否 | 与订阅名称不区分大小写完全匹配;省略以匹配所有 |
| `justification` | 是 | 自由文本 —— 在发现旁边的报告中显示 |
| `expires` | 是 | ISO 日期(`YYYY-MM-DD`);距今天最多 1 年 |
- 只有 `FAIL` 和 `ERROR` 发现可以被屏蔽
- 过期条目将被跳过,发现会恢复为 `FAIL`/`ERROR` 并记录警告
- 到期时间超过 1 年的条目将被限制为 1 年并发出警告
### 屏蔽工作流
```
# 1. 检查当前被屏蔽的内容
python cis_azure_audit.py --list-suppressions
# 2. 编辑 suppressions.toml,然后在不重新审计的情况下重新生成 report
python cis_azure_audit.py --report-only
# 3. 使用自定义 suppressions 文件(例如按环境配置)
python cis_azure_audit.py --suppressions prod-suppressions.toml
```
## 覆盖的控制项
### 第 2 部分 — Azure Databricks(6 项中有 5 项自动化)
| 控制项 | 标题 | 级别 |
| --- | --- | --- |
| 2.1.2 | 为 Databricks 子网配置 NSG | L1 |
| 2.1.7 | 已配置诊断日志记录 | L1 |
| 2.1.9 | 未启用公共 IP | L1 |
| 2.1.10 | 已禁用公共网络访问 | L1 |
| 2.1.11 | 使用专用终结点访问工作区 | L2 |
### 第 5 部分 — 身份服务(9 项自动化 · 2 项手动)
| 控制项 | 标题 | 级别 | 备注 |
| --- | --- | --- | --- |
| 5.1.1 | 已启用安全默认值 | L1 | 需要 `[graph_auth]` 设置 —— 见下文 |
| 5.1.2 | 为所有用户启用 MFA | L1 | |
| 5.1.3 | 禁止用户在受信任的设备上记住 MFA | L1 | **手动** —— 无 API 可用 |
| 5.3.3 | 限制用户访问管理员角色 | L1 | |
| 5.4 | 限制非管理员用户创建租户 | L1 | |
| 5.14 | 用户不能注册应用程序 | L1 | |
| 5.15 | 来宾访问仅限于自己的目录对象 | L1 | |
| 5.16 | 来宾邀请限制设置为管理员或无人 | L2 | |
| 5.23 | 无自定义订阅管理员角色 | L1 | |
| 5.27 | 订阅所有者在 2 到 3 人之间 | L1 | |
| 5.28 | 特权用户受防钓鱼 MFA 保护 | L1 | **手动** —— Entra ID 门户审查 |
#### 检查项 5.1.1 设置(一次性)
1. 在 Entra ID 中,前往 **App registrations** → **New registration**(任意名称,单租户)。
2. **API permissions** → Add → Microsoft Graph → **Delegated** → `Policy.Read.All` → **Grant admin consent**。
3. 对于服务主体 / CI:还需添加 **Application** 权限 `Policy.Read.All` 并同意;通过 `CIS_GRAPH_CLIENT_SECRET` **环境变量**设置机密(推荐 —— 避免在 `cis_audit.toml` 中存储凭据),或仅在本地使用时在 `[graph_auth]` 中设置 `client_secret`。
4. 将 **Application (client) ID** 复制到 `cis_audit.toml`:
[graph_auth]
client_id = "your-app-client-id"
5. `pip install -r requirements.txt`
6. 运行工具 —— 浏览器窗口打开一次用于交互式登录;后续运行使用
缓存在 `~/.cis_audit/msal_token_cache.json` 中的令牌。
### 第 6 部分 — 管理和治理(16 项自动化)
| 控制项 | 标题 | 级别 |
| --- | --- | --- |
| 6.1.1.1 | 订阅活动日志存在诊断设置 | L1 |
| 6.1.1.2 | 诊断设置捕获所需类别 | L1 |
| 6.1.1.4 | Key Vault 诊断日志记录已启用 | L1 |
| 6.1.1.6 | Azure AppService HTTP 日志已启用 | 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 |
### 第 7 部分 — 网络服务(13 项自动化)
| 控制项 | 标题 | 级别 |
| --- | --- | --- |
| 7.1 | RDP (3389) 未对互联网开放 | L1 |
| 7.2 | SSH (22) 未对互联网开放 | L1 |
| 7.3 | 来自互联网的 UDP 访问受限制 | L1 |
| 7.4 | 来自互联网的 HTTP/HTTPS (80/443) 已评估并受限制 | L1 |
| 7.5 | NSG 流日志保留期 >= 90 天 | L2 |
| 7.6 | 为所有使用中的区域启用 Network Watcher | L2 |
| 7.8 | VNet 流日志保留期 >= 90 天 | L2 |
| 7.10 | Azure Application Gateway 上启用了 WAF | L2 |
| 7.11 | 子网与 NSG 关联 | L1 |
| 7.12 | App Gateway SSL 策略最低 TLS 1.2+ | L1 |
| 7.13 | Application Gateway 上启用了 HTTP2 | L1 |
| 7.14 | 启用了 WAF 请求正文检查 | L2 |
| 7.15 | 启用了 WAF 机器人保护 | L2 |
### 第 8 部分 — 安全服务(30 项自动化)
| 控制项 | 标题 | 级别 |
| --- | --- | --- |
| 8.1.1.1 | Microsoft Defender CSPM | L2 |
| 8.1.2.1 | Microsoft Defender for APIs | L2 |
| 8.1.3.1 | Microsoft Defender for Servers | L2 |
| 8.1.3.3 | 端点保护 (WDATP) 组件 | L2 |
| 8.1.4.1 | Microsoft Defender for Containers | L2 |
| 8.1.5.1 | Microsoft Defender for Storage | 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.12 | 安全警报通知订阅所有者 | L1 |
| 8.1.13 | 安全联系人的其他电子邮件地址 | L1 |
| 8.1.14 | 已配置警报严重性通知 | L1 |
| 8.1.15 | 已配置攻击路径通知 | L1 |
| 8.3.1 | 已设置密钥过期 —— RBAC Key Vault | L1 |
| 8.3.2 | 已设置密钥过期 —— 非 RBAC Key Vault | L1 |
| 8.3.3 | 已设置机密过期 —— RBAC Key Vault | L1 |
| 8.3.4 | 已设置机密过期 —— 非 RBAC Key Vault | 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.11 | 证书有效期 <= 12 个月 | L1 |
| 8.4.1 | Azure Bastion Host 存在 | L2 |
| 8.5 | VNet 上启用了 DDoS Network Protection | L2 |
### 第 9 部分 — 存储服务(21 项自动化)
| 控制项 | 标题 | 级别 |
| --- | --- | --- |
| 9.1.1 | Azure Files 软删除已启用 | 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 | 允许 Azure 受信任服务访问存储 | L2 |
| 9.3.6 | 最低 TLS 版本 1.2 | L1 |
| 9.3.7 | 跨租户复制已禁用 | L1 |
| 9.3.8 | Blob 匿名访问已禁用 | L1 |
| 9.3.9 | 存储帐户具有 CanNotDelete 资源锁 | L1 |
| 9.3.10 | 存储帐户具有 ReadOnly 资源锁 | L2 |
| 9.3.11 | 冗余设置为异地冗余 (GRS) | L2 |
## 测试
测试套件使用 Python 内置的 `unittest` —— 不需要额外的包。
所有测试都模拟 Azure CLI 调用,因此不需要真正的 Azure 连接。
### 运行所有
```
python -m unittest discover -s tests -p "test_*.py" -v
```
### 运行一个测试文件
```
python -m unittest tests.test_checks -v
python -m unittest tests.test_report -v
```
### 运行一个测试类
```
python -m unittest tests.test_permissions.TestPreflight -v
```
### 持续集成
GitHub Actions 管道在每次推送和拉取请求时运行:
- `python -m unittest` (Python 3.10, 3.11, 3.12, 3.13)
- `black --check` 格式化
- `flake8` 代码检查
- `mypy` 静态类型检查
工作流文件位于 `.github/workflows/ci.yml`。
## 检查点文件
```
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` 丢弃所有检查点。
使用 `--report-only` 从现有检查点重新生成 HTML 报告,而不运行任何检查。
加载检查点时,结果会使用当前工具逻辑自动重新分类。
这意味着升级工具并运行 `--report-only` 会将新版本中修正的错误分类、
消息改进和控制元数据修复应用于您现有的检查点
数据 —— 在大多数情况下,不需要重新审计。
## 已知限制
**只读** —— 脚本仅进行审计。它不会对您的环境进行任何更改。
**时间点** —— 结果反映脚本运行时的状态。
**Key Vault 数据平面访问** —— 列出密钥、机密和证书除了订阅 Reader 外还需要数据平面
权限。分配 **Key Vault Reader**(RBAC 保管库)或将
运行帐户添加到保管库的访问策略(非 RBAC 保管库)。没有此项,受影响的检查
将返回 ERROR 并附带说明性消息。报告清楚地将此与正常
结果区分开来 —— 合规性未知,而非假定正常。
**检查项 5.1.1 返回 ERROR (Policy.Read.All)**
`az` CLI 应用无法获取 `Policy.Read.All`。这是 Microsoft 施加的限制 —
az CLI 应用(`04b07795-...`)未预授权该作用域。在
`cis_audit.toml` 中使用您自己的应用注册配置 `[graph_auth]` 以解决此问题(见上文“检查项 5.1.1 设置”)。
**用于身份检查的 Graph API** —— 控制项 5.4, 5.14, 5.15, 和 5.16 通过
`az rest` 调用 Graph API。如果 Azure CLI 应用尚未同意所需的 Graph 权限,这些
将返回 ERROR。使用以下命令测试:
```
az rest --method get --url "https://graph.microsoft.com/v1.0/policies/authorizationPolicy"
```
**条件访问策略 (5.2.x)** —— 在基准中标记为手动,此工具不检查。它们需要在 Entra ID 门户中进行审查。
**大型租户** —— Resource Graph 高效处理批量数据,权限预检
并行运行订阅。规模上的主要瓶颈是每个订阅的 CLI 调用。
使用 `--parallel 5` 或更高,或通过 `cis_audit.toml` 进行调整。
## 故障排除
**Windows 上找不到 `az`**
该脚本在 Windows 上自动使用 `az.cmd`。确保已安装 Azure CLI 并在您的
PATH 中,然后重新启动终端。
**身份检查返回 ERROR (AccessDenied)**
您的帐户在 Entra ID 中需要 Global Reader。直接测试 Graph 调用:
```
az rest --method get --url "https://graph.microsoft.com/v1.0/policies/authorizationPolicy"
```
如果失败,请让您的 Entra ID 管理员授予 Global Reader 或同意 Azure CLI 应用所需的 Graph API
权限(应用 ID:`04b07795-8ddb-461a-bbee-02f9e1bf7b46`)。
**5.1.1 特别返回 ERROR (Policy.Read.All)**
如果未配置 `[graph_auth]`,这是预期的。az CLI 应用无法获取此作用域。
见第 5 部分控制表下的“检查项 5.1.1 设置”。
**Key Vault 检查返回 ERROR(审计不完整)**
运行帐户需要 Key Vault 数据平面访问权限。对于启用 RBAC 的保管库,分配
**Key Vault Reader** 角色;对于访问策略保管库,将帐户添加到保管库的访问策略。
报告中的 ERROR 消息确切说明了缺少什么。
**某项检查持续超时**
所有 `az` CLI 调用都有可配置的超时(默认 20 秒)。在 `cis_audit.toml` 中增加它们:
```
[timeouts]
default = 40
```
超时的检查记录为 ERROR,审计继续。
**找不到订阅**
订阅名称完全匹配(区分大小写)。运行 `az account list --output table` 以
查看您的帐户可用的确切名称。
**运行中断**
按一次 Ctrl+C。进行中的 Azure CLI 调用立即被终止,
已完成订阅的检查点被保留,并且进程干净退出。重新运行相同的命令以
从中断处恢复,或添加 `--fresh` 重新开始。
## 免责声明
本工具**按“原样”提供,不附带任何形式的保证**。维护者和贡献者
不提供 SLA,不保证准确性或完整性,也不对
本工具的使用或基于其输出做出的任何决定承担法律责任。使用风险完全
由您自己承担。有关完整的 MIT 免责声明,请参阅 [LICENSE](LICENSE)。
## 致谢
这是一个引用公开可用的
**[CIS Microsoft Azure Foundations Benchmark v5.0.0](https://www.cisecurity.org/benchmark/azure)** 的独立社区实现。
CIS Benchmarks 是 Center for Internet Security () 的财产,
根据 [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) 使用。
本工具不隶属于 CIS、未经其认可或批准。
**版本:** 1.0.0-beta3
**基准:** CIS Microsoft Azure Foundations Benchmark v5.0.0 (2025年9月)
**覆盖范围:** 94项自动化控制 · 输出中注明了2项手动控制 · 1项控制待定 (2.1.1)
标签:ATTACK-Python-Client, Azure, Azure CLI, Azure租户, CIS Benchmark, DevSecOps, GPT, HTML报告, IT治理, Python, Web报告查看器, 上游代理, 云基础设施, 关系图谱, 反取证, 合规性扫描, 基线检查, 安全评估, 对称加密, 无后门, 漏洞管理, 自动化审计, 评分系统, 足迹分析, 逆向工具, 配置加固