| name | detecting-misconfigured-azure-storage |
| description | 使用 Azure CLI、PowerShell 和 Microsoft Defender for Storage,检测 Azure 存储账户错误配置,包括可公开访问的 blob 容器、缺失的加密设置、过于宽泛的 SAS 令牌、禁用的日志记录以及网络访问违规。
|
| domain | cybersecurity |
| subdomain | cloud-security |
| tags | ["cloud-security","azure","storage-security","blob-storage","sas-tokens","data-protection"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
检测 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:枚举所有存储账户及基本配置
列出跨订阅的所有存储账户,评估其基线安全设置。
az storage account list \
--query "[].{Name:name, ResourceGroup:resourceGroup, Location:location, Kind:kind, Sku:sku.name, HttpsOnly:enableHttpsTrafficOnly, MinTLS:minimumTlsVersion, PublicAccess:allowBlobPublicAccess}" \
-o table
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 数据的存储账户和容器。
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 范围内。
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 管理的密钥或客户管理的密钥)。
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
az storage account list \
--query "[?minimumTlsVersion!='TLS1_2'].{Name:name, TLS:minimumTlsVersion}" \
-o table
步骤 5:审计共享访问签名和访问密钥
识别权限过于宽泛的 SAS 令牌,并检查访问密钥使用模式。
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
az storage account list \
--query "[].{Name:name, AllowSharedKeyAccess:allowSharedKeyAccess}" \
-o table
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 诊断设置。
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
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 密钥和数据库连接字符串存储在可公开访问的容器中。
方法:
- 运行
az storage account list 过滤 allowBlobPublicAccess=true
- 枚举公开访问级别设置为
blob 或 container 的容器
- 列出公开容器的内容以识别敏感文件
- 检查 Defender for Storage 中来自异常 IP 的异常访问告警
- 立即将存储账户上的
allowBlobPublicAccess 设置为 false
- 轮换在公开容器中发现的所有已暴露凭据
- 启用网络 ACL,将访问限制为应用程序 VNet
- 为合法的公开内容分发配置 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