修正对 Blob 数据的匿名公共读取访问(Azure 资源管理器部署)

Azure Blob 存储支持对容器和 Blob 进行可选的匿名公共读取访问。 但是,匿名访问可能会带来安全风险。 建议禁用匿名访问,以获得最佳安全性。 禁止公共访问有助于防止意外的匿名访问产生的数据泄露。

默认情况下,始终会禁止对 blob 数据的公共访问。 但是,Azure 资源管理器存储帐户的默认配置,允许具有适当权限的用户配置对存储帐户中的容器和 Blob 的公共访问。 无论单个容器的公共访问设置如何,都可以通过将存储帐户上的 AllowBlobPublicAccess 属性设置为 False 来禁止对 Azure 资源管理器存储帐户的所有公共访问。

禁止对存储帐户的公共 Blob 访问后,Azure 存储会拒绝对该帐户的所有匿名请求。 禁止对存储帐户的公共访问会阻止用户随后为该帐户中的容器配置公共访问。 已配置了公共访问的所有容器将不再接受匿名请求。

警告

为容器配置了公共访问时,任何客户端都可以读取该容器中的数据。 公共访问会带来潜在的安全风险,因此,如果你的方案不需要公共访问,我们建议你为存储帐户禁止它。

Azure 资源管理器与经典存储帐户的修正

本文介绍如何使用 DRAG(Detection-Remediation-Audit-Governance,检测-修正-审核-治理)框架持续管理使用 Azure 资源管理器部署模型的存储帐户的公共访问。 所有常规用途 v2 存储帐户、高级块 Blob 存储帐户、高级文件共享帐户和 Blob 存储帐户都使用 Azure 资源管理器部署模型。 某些较旧的常规用途 v1 帐户和高级页 Blob 帐户可能使用经典部署模型。

如果存储帐户使用经典部署模型,建议尽快迁移到 Azure 资源管理器部署模型。 使用经典部署模型的 Azure 存储帐户将于 2024 年 8 月 31 日停用。 有关详细信息,请参阅 Azure 经典存储帐户将于 2024 年 8 月 31 日停用

如果目前无法迁移经典存储帐户,则应立即修正对这些帐户的公共访问。 若要了解如何修正对经典存储帐户的公共访问,请参阅修正对 Blob 数据的匿名公共读取访问(经典部署)。 有关 Azure 部署模型的详细信息,请参阅资源管理器和经典部署

关于匿名公共读取访问

默认情况下,系统始终禁止对你的数据进行匿名公共访问。 有两个单独的设置会影响公共访问:

  1. 允许对存储帐户进行公共访问。 默认情况下,存储帐户允许具有适当权限的用户启用对容器的公共访问。 Blob 数据不可供公共访问,除非用户采取额外步骤显式配置了容器的公共访问设置。
  2. 配置容器的公共访问设置。 默认情况下,容器的公共访问设置被禁用,这意味着对容器或其数据的每个请求都需要授权。 具有适当权限的用户可以修改容器的公共访问设置,以便仅在允许对存储帐户进行匿名访问时才启用匿名访问。

下表总结了这两个设置如何共同影响对容器的公共访问。

容器的公共访问级别设置为“专用”(默认设置) 容器的公共访问级别设置为“容器” 容器的公共访问级别设置为“Blob”
禁止对存储帐户进行公共访问 推荐。 不允许对存储帐户中的任何容器进行公共访问。 不允许对存储帐户中的任何容器进行公共访问。 存储帐户设置替代容器设置。 不允许对存储帐户中的任何容器进行公共访问。 存储帐户设置替代容器设置。
允许对存储帐户进行公共访问(默认设置) 不允许对此容器进行公共访问(默认配置)。 建议不要使用。 允许对此容器及其 blob 进行公共访问。 建议不要使用。 允许对此容器中的 blob 进行公共访问,但不允许对容器本身进行公共访问。

如果允许对存储帐户进行匿名公共访问,并为特定容器配置了匿名公共访问,该服务就会接受读取此容器中的 blob 的请求(该请求在没有授权标头的情况下传递),并且该 blob 的数据会在响应中返回。

检测来自客户端应用程序的匿名请求

如果你禁止对存储帐户的公共读取访问,系统可能会拒绝对当前配置了公共访问的容器和 blob 的请求。 禁止对存储帐户的公共访问将替代该存储帐户中各个容器的公共访问设置。 禁止对存储帐户的公共访问后,将来对该帐户的任何匿名请求都会失败。

为了理解禁止公共访问可能会对客户端应用程序造成的影响,我们建议你为该帐户启用日志记录和指标,并分析一段时间间隔内的匿名请求的模式。 使用指标来确定对存储帐户的匿名请求数,并使用日志来确定匿名访问了哪些容器。

使用指标资源管理器监视匿名请求

若要跟踪对存储帐户的匿名请求,请在 Azure 门户中使用 Azure 指标资源管理器。 若要详细了解 Azure 指标资源管理器,请参阅 Azure 指标资源管理器入门

按照以下步骤创建跟踪匿名请求的指标:

  1. 导航到 Azure 门户中的存储帐户。 在“监视”部分下,选择“指标” 。

  2. 选择“添加指标”。 在“指标”对话框中,指定以下值:

    1. 将“作用域”字段设置为存储帐户的名称。
    2. 将“指标命名空间”设置为“Blob”。 此指标仅报告对 Blob 存储的请求。
    3. 将“指标”字段设置为“事务”。
    4. 将“聚合”字段设置为“求和”。

    新指标会显示给定时间间隔内针对 Blob 存储的事务数之和。 生成的指标如下图所示:

    屏幕截图显示了如何将指标配置为对 blob 事务数求和

  3. 接下来,选择“添加筛选器”按钮,为匿名请求创建基于指标的筛选器。

  4. 在“筛选器”对话框中,指定以下值:

    1. 将属性值设置为“身份验证”。
    2. 将“运算符”字段设置为等号 (=)。
    3. 通过从下拉列表中选择或输入将“值”字段设置为“匿名”。
  5. 在右上角,选择要查看指标的时间间隔。 还可以通过指定从 1 分钟到 1 个月的时间间隔,来指示请求聚合粒度。

配置指标后,匿名请求将开始显示在图形上。 下图显示了在过去 30 分钟内聚合的匿名请求。

屏幕截图显示了对 Blob 存储的聚合匿名请求

你还可以配置警报规则,让系统在针对你的存储帐户发出的匿名请求达到一定数量时通知你。 有关详细信息,请参阅使用 Azure Monitor 创建、查看和管理指标警报

修正对存储帐户的匿名公共访问

评估对存储帐户中容器和 Blob 的匿名请求后,可以通过将该帐户的 AllowBlobPublicAccess 属性设置为 False 来采取措施,以修正对整个帐户的公共访问。

存储帐户的公共访问设置会替代该帐户中容器的单个设置。 如果你禁止对存储帐户的公共访问,则任何配置为允许公共访问的容器将不再可供以匿名方式访问。 如果已禁止对帐户的公共访问,则不需要禁用单个容器的公共访问。

如果你的方案要求某些容器可供公共访问,则应将这些容器及其 Blob 移动到保留用于公共访问的独立存储帐户中。 然后,你可以禁止对任何其他存储帐户的公共访问。

重要

为存储帐户禁止匿名公共访问后,使用匿名持有者质询的客户端将会发现 Azure 存储返回 403 错误(禁止),而不是 401 错误(未授权)。 建议将所有容器设为专用以缓解此问题。 有关修改容器的公共访问设置的详细信息,请参阅设置容器的公共访问级别

修正 Blob 公共访问需要 Azure 存储资源提供程序的 2019-04-01 版或更高版本。 有关详细信息,请参阅 Azure 存储资源提供程序 REST API

禁止公共访问时所需的权限

若要为存储帐户设置 AllowBlobPublicAccess 属性,用户必须有权创建和管理存储帐户。 提供这些权限的 Azure 基于角色的访问控制 (Azure RBAC) 角色包含 Microsoft.Storage/storageAccounts/write 操作。 具有此操作的内置角色包括:

角色分配的范围必须设定为存储帐户级别或更高级别,以允许用户禁止对存储帐户的公共访问。 有关角色范围的详细信息,请参阅了解 Azure RBAC 的范围

注意,请仅向需要创建存储帐户或更新其属性的权限的那些管理用户分配这些角色。 使用最小特权原则确保用户拥有完成任务所需的最少权限。 有关使用 Azure RBAC 管理访问权限的详细信息,请参阅 Azure RBAC 最佳做法

这些角色不提供通过 Azure Active Directory (Azure AD) 对存储帐户中数据的访问权限。 但是,它们包含 Microsoft.Storage/storageAccounts/listkeys/action,可以授予对帐户访问密钥的访问权限。 借助此权限,用户可以使用帐户访问密钥访问存储帐户中的所有数据。

Microsoft.Storage/storageAccounts/listkeys/action 本身通过帐户密钥授予数据访问权限,但不授予用户更改存储帐户的 AllowBlobPublicAccess 属性的能力。 对于需要访问你的存储帐户中的数据但不应能够更改该存储帐户配置的用户,请考虑分配存储 Blob 数据参与者存储 Blob 数据读取者读取者和数据访问等角色。

注意

经典订阅管理员角色“服务管理员”和“共同管理员”具有 Azure 资源管理器所有者角色的等效权限。 “所有者”角色包含所有操作,因此具有这些管理角色之一的用户也可以创建存储帐户和管理帐户配置。 有关详细信息,请参阅经典订阅管理员角色、Azure 角色和 Azure AD 管理员角色

将存储帐户的 AllowBlobPublicAccess 属性设置为 False

若要禁止对存储帐户的公共访问,请将帐户的 AllowBlobPublicAccess 属性设置为 False。 此属性适用于使用 Azure 资源管理器部署模型创建的所有存储帐户。 有关详细信息,请参阅存储帐户概述

默认情况下,不会为存储帐户设置 AllowBlobPublicAccess 属性,在你显式设置此属性之前,它不会返回值。 当此属性值为 null 或为 true 时,存储帐户允许公共访问。

重要

禁止对存储帐户的公共访问将替代该存储帐户中所有容器的公共访问设置。 禁止对存储帐户的公共访问后,将来对该帐户的任何匿名请求都会失败。 在更改此设置之前,请务必按照检测来自客户端应用程序的匿名请求中所述的步骤,了解可能对匿名访问存储帐户中数据的客户端应用程序的影响。

若要在 Azure 门户中禁止对存储帐户的公共访问,请执行以下步骤:

  1. 导航到 Azure 门户中的存储帐户。

  2. 在“设置”下找到“配置”设置。

  3. 将“Blob 公共访问”设置为“禁用”。

    显示如何禁止对帐户的 Blob 公共访问的屏幕截图

注意

禁止对存储帐户的公共访问不会影响该存储帐户中承载的任何静态网站。 $web 容器始终可供公共访问。

更新存储帐户的公共访问设置后,最多可能需要 30 秒才能完全传播更改。

批量修正的示例脚本

以下示例 PowerShell 脚本针对订阅中的所有 Azure 资源管理器存储帐户运行,并将这些帐户的 AllowBlobPublicAccess 设置设为 False。

<#
.SYNOPSIS
Finds storage accounts in a subscription where AllowBlobPublicAccess is True or null.

.DESCRIPTION
This script runs against all Azure Resource Manager storage accounts in a subscription
and sets the "AllowBlobPublicAccess" property to False.

Standard operation will enumerate all accounts where the setting is enabled and allow the 
user to decide whether or not to disable the setting.  

Classic storage accounts will require individual adjustment of containers to remove public
access, and will not be affected by this script.

Run with BypassConfirmation=$true if you wish to disallow public access on all Azure Resource Manager 
storage accounts without individual confirmation.

You will need access to the subscription to run the script.

.PARAMETER BypassConformation
Set this to $true to skip confirmation of changes. Not recommended.

.PARAMETER SubscriptionId
The subscription ID of the subscription to check.

.PARAMETER ReadOnly
Set this parameter so that the script makes no changes to any subscriptions and only reports affect accounts.

.PARAMETER NoSignin
Set this parameter so that no sign-in occurs -- you must sign in first. Use this if you're invoking this script repeatedly for multiple subscriptions and want to avoid being prompted to sign-in for each subscription.

.OUTPUTS
This command produces only STDOUT output (not standard PowerShell) with information about affect accounts.
#>
param(
    [boolean]$BypassConfirmation=$false,
    [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName='SubscriptionId')]
    [String] $SubscriptionId,
    [switch] $ReadOnly, # Use this if you don't want to make changes, but want to get information about affected accounts
    [switch] $NoSignin # Use this if you are already signed in and don't want to be prompted again
)

begin {
    if ( ! $NoSignin.IsPresent ) {
        login-azaccount -Environment AzureChinaCloud | out-null
    }
}

process {
    Write-Host "NOTE: If you are using OAuth authorization on a storage account, disabling public access at the account level may interfere with authorization."

    try {
        select-azsubscription -subscriptionid $SubscriptionId -erroraction stop | out-null
    } catch {
        write-error "Unable to access select subscription '$SubscriptionId' as the signed in user -- ensure that you have access to this subscription." -erroraction stop
    }

    foreach ($account in Get-AzStorageAccount) 
    {
        if($account.AllowBlobPublicAccess -eq $null -or $account.AllowBlobPublicAccess -eq $true)
        {
            Write-host "Account:" $account.StorageAccountName " is not disallowing public access."

            if ( ! $ReadOnly.IsPresent ) {
                if(!$BypassConfirmation)
                {
                    $confirmation = Read-Host "Do you wish to disallow public access? [y/n]"
                }
                if($BypassConfirmation -or $confirmation -eq 'y')
                {
                    try
                    {
                        set-AzStorageAccount -Name $account.StorageAccountName -ResourceGroupName $account.ResourceGroupName -AllowBlobPublicAccess $false
                        Write-Host "Success!"
                    }
                    catch
                    {
                        Write-output $_
                    }
                }
            }
        }
        elseif($account.AllowBlobPublicAccess -eq $false)
        {
            Write-Host "Account:" $account.StorageAccountName " has public access disabled, no action required."
        }
        else
        {
            Write-Host "Account:" $account.StorageAccountName ". Error, please manually investigate."
        }
    }
}

end {
    Write-Host "Script complete"
}

验证是否已修正匿名访问

若要验证是否已修正对存储帐户的匿名访问,可以测试不允许匿名访问 Blob、不允许修改容器的公共访问设置以及无法创建启用了匿名访问的容器。

验证是否不允许对 blob 进行公共访问

若要验证是否禁止对特定 blob 进行公共访问,你可以尝试通过其 URL 下载 blob。 如果下载成功,则 blob 仍可供公共使用。 如果 blob 因为对存储帐户的公共访问被禁止而不可公共访问,则你将看到一条错误消息,指示此存储帐户不允许公共访问。

下面的示例演示了如何使用 PowerShell 来尝试通过 URL 下载 blob。 请记得将括号中的占位符值替换为你自己的值:

$url = "<absolute-url-to-blob>"
$downloadTo = "<file-path-for-download>"
Invoke-WebRequest -Uri $url -OutFile $downloadTo -ErrorAction Stop

验证是否不允许修改容器的公共访问设置

若要验证在禁止对存储帐户的公共访问后是否无法修改容器的公共访问设置,可以尝试修改该设置。 如果禁止对存储帐户的公共访问,则更改容器的公共访问设置将失败。

下面的示例演示了如何使用 PowerShell 来尝试更改容器的公共访问设置。 请记得将括号中的占位符值替换为你自己的值:

$rgName = "<resource-group>"
$accountName = "<storage-account>"
$containerName = "<container-name>"

$storageAccount = Get-AzStorageAccount -ResourceGroupName $rgName -Name $accountName
$ctx = $storageAccount.Context

Set-AzStorageContainerAcl -Context $ctx -Container $containerName -Permission Blob

验证是否不允许创建启用了公共访问的容器

如果禁止对存储帐户的公共访问,则你将无法创建启用了公共访问的新容器。 若要进行验证,你可以尝试创建启用了公共访问的容器。

下面的示例演示了如何使用 PowerShell 来尝试创建启用了公共访问的容器。 请记得将括号中的占位符值替换为你自己的值:

$rgName = "<resource-group>"
$accountName = "<storage-account>"
$containerName = "<container-name>"

$storageAccount = Get-AzStorageAccount -ResourceGroupName $rgName -Name $accountName
$ctx = $storageAccount.Context

New-AzStorageContainer -Name $containerName -Permission Blob -Context $ctx

检查多个帐户的公共访问设置

若要检查具有最佳性能的一组存储帐户的公共访问设置,可以使用 Azure 门户中的 Azure Resource Graph 资源管理器。 若要详细了解如何使用 Resource Graph 资源管理器,请参阅快速入门:使用 Azure Resource Graph 资源管理器运行你的第一个 Resource Graph 查询

默认情况下,不会为存储帐户设置 AllowBlobPublicAccess 属性,在你显式设置此属性之前,它不会返回值。 当此属性值为 null 或为 true 时,存储帐户允许公共访问。

在 Resource Graph 资源管理器中运行以下查询会返回存储帐户的列表,并显示每个帐户的公共访问设置:

resources
| where type =~ 'Microsoft.Storage/storageAccounts'
| extend allowBlobPublicAccess = parse_json(properties).allowBlobPublicAccess
| project subscriptionId, resourceGroup, name, allowBlobPublicAccess

下图显示了整个订阅中的查询结果。 请注意,对于已显式设置了 AllowBlobPublicAccess 属性的存储帐户,它在结果中显示为 true 或 false。 如果尚未为存储帐户设置 AllowBlobPublicAccess 属性,则它在查询结果中显示为空白(或 null)。

屏幕截图显示了在各个存储帐户中针对公共访问设置的查询结果

使用 Azure Policy 审核合规性

如果你有大量存储帐户,则可能需要执行审核,以确保将这些帐户配置为阻止公共访问。 若要审核一组存储帐户的合规性,请使用 Azure Policy。 Azure Policy 是一项服务,可用于创建、分配和管理将规则应用于 Azure 资源的策略。 Azure Policy 可帮助你确保这些资源始终符合公司标准和服务级别协议。 有关详细信息,请参阅 Azure Policy 概述

创建具有 Audit 效果的策略

Azure Policy 支持的效果决定了针对资源评估某个策略规则时会发生什么情况。 当资源不合规时,Audit 效果会创建一个警告,但不会停止请求。 有关效果的详细信息,请参阅了解 Azure Policy 效果

若要通过 Azure 门户为存储帐户的公共访问设置创建具有“审核”效果的策略,请执行以下步骤:

  1. 在 Azure 门户中,导航到 Azure Policy 服务。

  2. 在“创作”部分下,选择“定义”。

  3. 选择“添加策略定义”以创建新的策略定义。

  4. 对于“定义位置”字段,选择“更多”按钮以指定审核策略资源所在的位置。

  5. 指定策略的名称。 还可以指定说明和类别。

  6. 在“策略规则”下,将以下策略定义添加到“policyRule”部分。

    {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Storage/storageAccounts"
          },
          {
            "not": {
              "field":"Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
              "equals": "false"
            }
          }
        ]
      },
      "then": {
        "effect": "audit"
      }
    }
    
  7. 保存策略。

分配策略

接下来,将策略分配给资源。 策略的作用域对应于该资源及其下的所有资源。 有关策略分配的详细信息,请参阅 Azure Policy 分配结构

若要在 Azure 门户中分配策略,请执行下列步骤:

  1. 在 Azure 门户中,导航到 Azure Policy 服务。
  2. 在“创作”部分下,选择“分配”。
  3. 选择“分配策略”以创建新的策略分配。
  4. 对于“作用域”字段,请选择策略分配的作用域。
  5. 对于“策略定义”字段,请选择“更多”按钮,然后从列表中选择你在上一部分定义的策略。
  6. 提供策略分配的名称。 说明是可选的。
  7. 让“策略强制实施”设置为“启用”状态。 此设置对审核策略没有影响。
  8. 选择“查看 + 创建”以创建分配。

查看合规性报告

分配策略后,你可以查看合规性报告。 审核策略的合规性报告会指出哪些存储帐户不符合策略。 有关详细信息,请参阅获取策略合规性数据

创建策略分配后,合规性报告可能需要几分钟时间才会变得可用。

若要在 Azure 门户中查看合规性报告,请执行以下步骤:

  1. 在 Azure 门户中,导航到 Azure Policy 服务。

  2. 选择“合规性”。

  3. 筛选你在上一步创建的策略分配名称的结果。 该报告显示有多少资源不符合策略。

  4. 你可以在报告中向下钻取以获取更多详细信息,包括不合规的存储帐户的列表。

    屏幕截图显示了针对 blob 公共访问的审核策略的合规性报告

使用 Azure Policy 强制实施授权访问

Azure Policy 可以确保 Azure 资源符合要求和标准,从而为云治理提供支持。 若要确保组织中的存储帐户仅允许经授权的请求,你可以创建一个策略,用以阻止创建其公共访问设置允许匿名请求的新存储帐户。 如果该帐户的公共访问设置不符合策略,则此策略还会阻止对现有帐户进行的所有配置更改。

强制实施策略会使用“拒绝”效果来阻止将创建或修改一个允许公共访问的存储帐户的请求。 有关效果的详细信息,请参阅了解 Azure Policy 效果

若要为允许匿名请求的公共访问设置创建具有“拒绝”效果的策略,请执行使用 Azure Policy 审核合规性中所述的步骤,但在策略定义的“policyRule”节中提供以下 JSON:

{
  "if": {
    "allOf": [
      {
        "field": "type",
        "equals": "Microsoft.Storage/storageAccounts"
      },
      {
        "not": {
          "field":"Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
          "equals": "false"
        }
      }
    ]
  },
  "then": {
    "effect": "deny"
  }
}

创建具有“拒绝”效果的策略并将其分配给作用域后,用户无法创建允许公共访问的存储帐户。 用户也不能对当前允许公共访问的现有存储帐户进行任何配置更改。 如果尝试这样做,将会导致错误。 必须将存储帐户的公共访问设置设为 false,然后才能继续创建或配置帐户。

下图显示了在以下情况下发生的错误:当具有“拒绝”效果的策略要求禁止公共访问时,你尝试创建允许公共访问(针对新帐户的默认设置)的存储帐户。

屏幕截图显示了在违反策略的情况下创建存储帐户时出现的错误

后续步骤