# Detecting Misconfigured Azure Storage

> 使用 Azure CLI、PowerShell 和 Microsoft Defender for Storage，检测 Azure 存储账户错误配置，包括可公开访问的 blob 容器、缺失的加密设置、过于宽泛的 SAS 令牌、禁用的日志记录以及网络访问违规。

- Skill: `killvxk/detecting-misconfigured-azure-storage` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add killvxk/detecting-misconfigured-azure-storage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/killvxk/detecting-misconfigured-azure-storage/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: Apache-2.0
- Author: killvxk (https://skillmd.com/u/killvxk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/killvxk/detecting-misconfigured-azure-storage

---


# 检测 Azure 存储错误配置

## 适用场景

- 对订阅中的 Azure 存储账户进行安全审计时
- 响应 Microsoft Defender for Storage 发出的匿名访问或数据泄露告警时
- 合规要求需要验证加密、网络限制和访问日志记录时
- 调查通过可公开访问的 blob 容器造成的潜在数据暴露时
- 接入 Azure 订阅并建立存储安全基线时

**不适用于**：Azure SQL 或 Cosmos DB 安全审计（使用专用数据库安全工具）、存储操作的实时威胁检测（使用 Defender for Storage），或在不做相应调整的情况下对 Azure Files 或 Data Lake Gen2 进行专项审计。

## 前置条件

- 安装 Azure CLI 并通过 `az login` 完成认证，具备读者和存储账户贡献者角色
- 安装 Az PowerShell 模块用于高级查询（`Install-Module Az.Storage`）
- 启用 Microsoft Defender for Storage 进行威胁检测
- 可访问 Azure Resource Graph 以进行跨订阅查询
- 使用 ScoutSuite 或 Prowler Azure 提供商进行自动化评估

## 工作流程

### 步骤 1：枚举所有存储账户及基本配置

列出跨订阅的所有存储账户，评估其基线安全设置。

```bash
# 列出所有订阅的存储账户
az storage account list \
  --query "[].{Name:name, ResourceGroup:resourceGroup, Location:location, Kind:kind, Sku:sku.name, HttpsOnly:enableHttpsTrafficOnly, MinTLS:minimumTlsVersion, PublicAccess:allowBlobPublicAccess}" \
  -o table

# 使用 Resource Graph 进行跨订阅枚举
az graph query -q "
  Resources
  | where type == 'microsoft.storage/storageaccounts'
  | project name, resourceGroup, subscriptionId, location,
    properties.allowBlobPublicAccess,
    properties.enableHttpsTrafficOnly,
    properties.minimumTlsVersion,
    properties.networkAcls.defaultAction
" -o table
```

### 步骤 2：检测可公开访问的 Blob 容器

识别允许匿名公开访问 blob 数据的存储账户和容器。

```bash
# 检查每个存储账户的公开 blob 访问设置
for account in $(az storage account list --query "[].name" -o tsv); do
  public=$(az storage account show --name "$account" --query "allowBlobPublicAccess" -o tsv)
  echo "$account: allowBlobPublicAccess=$public"
done

# 列出已设置公开访问级别的容器
for account in $(az storage account list --query "[?allowBlobPublicAccess==true].name" -o tsv); do
  key=$(az storage account keys list --account-name "$account" --query "[0].value" -o tsv)
  echo "=== $account ==="
  az storage container list \
    --account-name "$account" \
    --account-key "$key" \
    --query "[?properties.publicAccess!='off' && properties.publicAccess!=null].{Container:name, PublicAccess:properties.publicAccess}" \
    -o table 2>/dev/null
done

# 测试对已发现公开容器的匿名访问
curl -s "https://ACCOUNT.blob.core.windows.net/CONTAINER?restype=container&comp=list" | head -50
```

### 步骤 3：审计网络访问和防火墙规则

检查存储账户是否对所有网络开放，还是限制在特定 VNet 或 IP 范围内。

```bash
# 查找默认网络操作设置为 Allow（对所有网络开放）的存储账户
az storage account list \
  --query "[?networkRuleSet.defaultAction=='Allow'].{Name:name, DefaultAction:networkRuleSet.defaultAction, VNetRules:networkRuleSet.virtualNetworkRules}" \
  -o table

# 详细的网络规则审计
for account in $(az storage account list --query "[].name" -o tsv); do
  echo "=== $account ==="
  az storage account show --name "$account" \
    --query "{DefaultAction:networkRuleSet.defaultAction, IPRules:networkRuleSet.ipRules[*].ipAddressOrRange, VNetRules:networkRuleSet.virtualNetworkRules[*].virtualNetworkResourceId, Bypass:networkRuleSet.bypass}" \
    -o json
done

# 查找具有私有端点的存储账户
az network private-endpoint list \
  --query "[?privateLinkServiceConnections[0].groupIds[0]=='blob'].{Name:name, Storage:privateLinkServiceConnections[0].privateLinkServiceId}" \
  -o table
```

### 步骤 4：验证加密设置和密钥管理

确保所有存储账户使用静态加密，并采用适当的密钥管理方式（Microsoft 管理的密钥或客户管理的密钥）。

```bash
# 检查所有存储账户的加密配置
for account in $(az storage account list --query "[].name" -o tsv); do
  echo "=== $account ==="
  az storage account show --name "$account" \
    --query "{Encryption:encryption.services, KeySource:encryption.keySource, KeyVaultUri:encryption.keyVaultProperties.keyVaultUri, InfraEncryption:encryption.requireInfrastructureEncryption}" \
    -o json
done

# 查找未启用基础设施加密（双重加密）的账户
az storage account list \
  --query "[?encryption.requireInfrastructureEncryption!=true].{Name:name, KeySource:encryption.keySource}" \
  -o table

# 检查 TLS 版本低于 1.2 的账户
az storage account list \
  --query "[?minimumTlsVersion!='TLS1_2'].{Name:name, TLS:minimumTlsVersion}" \
  -o table
```

### 步骤 5：审计共享访问签名和访问密钥

识别权限过于宽泛的 SAS 令牌，并检查访问密钥使用模式。

```bash
# 检查存储账户密钥的上次轮换时间
for account in $(az storage account list --query "[].name" -o tsv); do
  echo "=== $account ==="
  az storage account keys list \
    --account-name "$account" \
    --query "[].{KeyName:keyName, CreationTime:creationTime}" \
    -o table
done

# 检查存储账户是否允许共享密钥访问（应禁用，改用仅 AAD）
az storage account list \
  --query "[].{Name:name, AllowSharedKeyAccess:allowSharedKeyAccess}" \
  -o table

# 审查容器上的存储访问策略（SAS 治理）
for account in $(az storage account list --query "[].name" -o tsv); do
  key=$(az storage account keys list --account-name "$account" --query "[0].value" -o tsv 2>/dev/null)
  for container in $(az storage container list --account-name "$account" --account-key "$key" --query "[].name" -o tsv 2>/dev/null); do
    policies=$(az storage container policy list --container-name "$container" --account-name "$account" --account-key "$key" 2>/dev/null)
    [ -n "$policies" ] && echo "$account/$container: $policies"
  done
done
```

### 步骤 6：检查诊断日志记录和监控

验证是否启用了存储分析日志记录和 Azure Monitor 诊断设置。

```bash
# 检查存储账户的诊断设置
for account in $(az storage account list --query "[].name" -o tsv); do
  rg=$(az storage account show --name "$account" --query "resourceGroup" -o tsv)
  echo "=== $account ==="
  az monitor diagnostic-settings list \
    --resource "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$rg/providers/Microsoft.Storage/storageAccounts/$account" \
    --query "[].{Name:name, Logs:logs[*].category, Metrics:metrics[*].category}" \
    -o json 2>/dev/null || echo "  未配置诊断设置"
done

# 检查 blob 服务日志记录属性
for account in $(az storage account list --query "[].name" -o tsv); do
  key=$(az storage account keys list --account-name "$account" --query "[0].value" -o tsv 2>/dev/null)
  az storage logging show \
    --account-name "$account" \
    --account-key "$key" \
    --services b 2>/dev/null
done
```

## 核心概念

| 术语 | 定义 |
|------|------|
| Blob 公开访问（Blob Public Access） | 存储账户设置，允许匿名读取 blob 容器及其内容，无需身份验证 |
| 共享访问签名（Shared Access Signature） | 带有嵌入式身份验证令牌的有时限 URI，授予对 Azure 存储资源的委托访问权限和特定权限 |
| 网络 ACL 默认操作（Network ACL Default Action） | 存储防火墙设置，决定是否默认允许或拒绝流量，特定 IP 和 VNet 可设为例外 |
| 客户管理的密钥（Customer-Managed Key） | 存储在 Azure Key Vault 中、由客户控制的加密密钥，用于存储加密而不是使用 Microsoft 管理的密钥 |
| 存储访问策略（Stored Access Policy） | 容器上命名的策略，定义 SAS 权限、起止时间，可独立撤销而不影响单个 SAS 令牌 |
| Defender for Storage | Microsoft Defender 计划，为异常存储访问模式、恶意软件上传和数据泄露提供威胁检测 |

## 工具与系统

- **Azure CLI**：用于查询存储账户配置、容器和访问策略的主要工具
- **Azure Resource Graph**：跨订阅查询引擎，可高效地大规模枚举存储安全设置
- **Microsoft Defender for Storage**：威胁检测服务，识别异常访问模式和潜在数据泄露
- **Prowler Azure**：开源工具，包含符合 CIS Azure Foundations 标准的自动化存储安全检查
- **ScoutSuite**：多云审计工具，包含针对公开访问、加密和网络的 Azure 存储专项检查

## 常见场景

### 场景：检测由开发人员错误配置导致的存储账户暴露

**场景背景**：开发人员为 Web 应用创建存储账户，并启用 blob 公开访问以提供静态文件服务。他们不小心将 API 密钥和数据库连接字符串存储在可公开访问的容器中。

**方法**：
1. 运行 `az storage account list` 过滤 `allowBlobPublicAccess=true`
2. 枚举公开访问级别设置为 `blob` 或 `container` 的容器
3. 列出公开容器的内容以识别敏感文件
4. 检查 Defender for Storage 中来自异常 IP 的异常访问告警
5. 立即将存储账户上的 `allowBlobPublicAccess` 设置为 `false`
6. 轮换在公开容器中发现的所有已暴露凭据
7. 启用网络 ACL，将访问限制为应用程序 VNet
8. 为合法的公开内容分发配置 Azure CDN 或 Front Door

**常见陷阱**：立即禁用 blob 公开访问会破坏公开提供内容的应用程序。应与开发团队协调，在禁用公开访问前先实施 Azure CDN。密钥轮换前生成的 SAS 令牌在过期前仍然有效，除非重新生成底层存储密钥。

## 输出格式

```
Azure 存储安全审计报告
======================================
订阅: 生产环境（SUB-ID）
评估日期: 2026-02-23
已审计存储账户: 24 个

严重发现:
[STOR-001] 已启用公开 Blob 访问
  账户: webapp-static-prod
  容器: uploads（PublicAccess: blob）
  风险: 匿名用户可读取容器中的所有 blob
  内容: 1,247 个文件，包含 .env 和 config.json
  修复建议: 禁用 allowBlobPublicAccess，改用带 SAS 的 Azure CDN

[STOR-002] 存储账户对所有网络开放
  账户: data-lake-analytics
  默认操作: Allow（无网络限制）
  风险: 可从任何网络（包括互联网）访问
  修复建议: 将默认操作设置为 Deny，添加 VNet 规则

摘要:
  已启用公开 blob 访问:      3 / 24
  对所有网络开放:            8 / 24
  缺少基础设施加密:         12 / 24
  TLS 版本低于 1.2:          2 / 24
  未启用诊断日志:           10 / 24
  已启用共享密钥访问:       18 / 24
  密钥未在 90 天内轮换:     14 / 24
```

