你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

安全公告:更新 Microsoft Entra 身份验证的角色分配

发现了一个影响沉浸式阅读器的 Microsoft Entra 身份验证的安全 bug。 建议更改沉浸式阅读器资源的权限。

背景

当你最初创建沉浸式阅读器资源并配置它们以进行 Microsoft Entra 身份验证时,需要授予 Microsoft Entra 应用程序标识访问沉浸式阅读器资源的权限。 这称为角色分配。 以前用于授予权限的 Azure 角色是认知服务用户角色。

在安全审核过程中,发现此认知服务用户角色具有列出密钥的权限。 这有点令人担心,因为沉浸式阅读器集成涉及在客户端 Web 应用和浏览器中使用此 Microsoft Entra 访问令牌。 如果访问令牌被不良参与者或攻击者窃取,则此访问令牌恐怕会被用于为你的沉浸式阅读器资源list keys。 如果攻击者可以列出资源的密钥,则他们将获得资源的订阅密钥。 资源的订阅密钥用作身份验证机制,并被视为机密。 如果攻击者拥有资源的订阅密钥,他们将可以对沉浸式阅读器资源终结点进行有效且经过身份验证的 API 调用,从而可能会因终结点上的使用量增加和限制而导致拒绝服务。 它还允许未经授权使用沉浸式阅读器资源,从而将导致账单费用增加。

然而在实际中,这种攻击或利用不太可能发生或甚至不可能发生。 对于沉浸式阅读器场景,客户获取具有 https://cognitiveservices.azure.com 受众的 Microsoft Entra 访问令牌。 为了成功为资源执行 list keys 操作,Microsoft Entra 访问令牌需要具有 https://management.azure.com 的受众。 一般来说,这不是太大的问题,因为用于沉浸式阅读器场景的访问令牌无法list keys,因为它们没有所需的受众。 为了更改访问令牌的受众,攻击者必须在调用 Microsoft Entra ID 以获取令牌之前,先劫持令牌获取代码并更改受众。 同样,这不太可能被利用,我们建议客户在 Web 应用后端而不是在客户端或浏览器中创建 Microsoft Entra 访问令牌,这是沉浸式阅读器身份验证的最佳做法。 在这些情况下,由于令牌获取发生在后端服务上,因此攻击者不太可能或甚至不可能入侵该进程并改变受众。

真正的问题是何时或是否有任何客户直接在客户端代码中从 Microsoft Entra ID 获取令牌。 我们强烈建议不要这样做,但由于客户可以随意按照自己认为合适的方式进行实现,因此一些客户可能正在这样做。

为了减轻对任何可能使用 Microsoft Entra 访问令牌执行 list keys 操作的担忧,我们创建了一个新的内置 Azure 角色,称为 Cognitive Services Immersive Reader User,它没有 list keys 权限。 此新角色与 Cognitive Services User 角色不同,它不是 Azure AI 服务平台的共享角色。 此新角色特定于沉浸式阅读器,并仅允许调用沉浸式阅读器 API。

我们建议所有客户使用新的 Cognitive Services Immersive Reader User 角色而不是原始的 Cognitive Services User 角色。 我们在下面提供了一个脚本,你可以针对每个资源运行该脚本来切换角色分配权限。

此建议适用于所有客户,以确保为所有人修补此漏洞,无论实现方案或攻击可能性如何。

如果你不这样做,也没有问题。 旧角色将继续发挥作用。 对于大多数客户而言,安全影响都很小。 但是,建议迁移到新角色,以缓解讨论到的安全问题。 应用此更新是一项安全咨询建议,而不是强制性的。

如何:创建沉浸式阅读器资源中使用脚本创建的任何新沉浸式阅读器资源都会自动使用新角色。

更新角色并轮换订阅密钥

如果在 2022 年 2 月之前使用如何:创建沉浸式阅读器资源中的说明创建和配置了沉浸式阅读器资源,建议执行以下操作来更新所有沉浸式阅读器资源的角色分配权限。 该操作涉及运行脚本以更新单个资源的角色分配。 如果有多个资源,请多次运行此脚本,针对每个资源运行一次。

使用以下脚本更新角色后,我们还建议在资源上轮换订阅密钥。 这是为了防止你的密钥被漏洞泄露,从而导致有人在未经你同意的情况下使用你的资源进行订阅密钥身份验证。 轮换密钥会使先前的密钥无效并拒绝任何进一步的访问。 对于使用 Microsoft Entra 身份验证的客户(这应该是当前沉浸式阅读器 SDK 实现的每个人),轮换密钥不会影响沉浸式阅读器服务,因为 Microsoft Entra 访问令牌用于身份验证,而不是订阅密钥。 轮换订阅密钥只是另一项预防措施。

可以在 Azure 门户中轮换订阅密钥。 导航到资源,然后找到 Keys and Endpoint 部分。 在顶部有 Regenerate Key1Regenerate Key2 按钮。

Azure 门户的屏幕截图,显示了一个沉浸式阅读器资源,其中选择了“密钥和终结点”部分(顶部显示了“重新生成密钥”按钮)。

使用 Azure PowerShell 更新角色分配

  1. 首先打开 Azure Cloud Shell。 请确保在左上角下拉列表中或键入 pwsh 时,将 Cloud Shell 设置为 PowerShell。

  2. 将以下代码片段复制并粘贴到 shell 中。

    function Update-ImmersiveReaderRoleAssignment(
        [Parameter(Mandatory=$true, Position=0)] [String] $SubscriptionName,
        [Parameter(Mandatory=$true)] [String] $ResourceGroupName,
        [Parameter(Mandatory=$true)] [String] $ResourceName,
        [Parameter(Mandatory=$true)] [String] $AADAppIdentifierUri
    )
    {
        $unused = ''
        if (-not [System.Uri]::TryCreate($AADAppIdentifierUri, [System.UriKind]::Absolute, [ref] $unused)) {
            throw "Error: AADAppIdentifierUri must be a valid URI"
        }
    
        Write-Host "Setting the active subscription to '$SubscriptionName'"
        $subscriptionExists = Get-AzSubscription -SubscriptionName $SubscriptionName
        if (-not $subscriptionExists) {
            throw "Error: Subscription does not exist"
        }
        az account set --subscription $SubscriptionName
    
        # Get the Immersive Reader resource 
        $resourceId = az cognitiveservices account show --resource-group $ResourceGroupName --name $ResourceName --query "id" -o tsv
        if (-not $resourceId) {
            throw "Error: Failed to find Immersive Reader resource"
        }
    
        # Get the Microsoft Entra application service principal
        $principalId = az ad sp show --id $AADAppIdentifierUri --query "objectId" -o tsv
        if (-not $principalId) {
            throw "Error: Failed to find Microsoft Entra application service principal"
        }
    
        $newRoleName = "Cognitive Services Immersive Reader User"
        $newRoleExists = az role assignment list --assignee $principalId --scope $resourceId --role $newRoleName --query "[].id" -o tsv
        if ($newRoleExists) {
            Write-Host "New role assignment for '$newRoleName' role already exists on resource"
        } 
        else {
            Write-Host "Creating new role assignment for '$newRoleName' role"
            $roleCreateResult = az role assignment create --assignee $principalId --scope $resourceId --role $newRoleName
            if (-not $roleCreateResult) {
                throw "Error: Failed to add new role assignment"
            }
            Write-Host "New role assignment created successfully"
        }
    
        $oldRoleName = "Cognitive Services User"
        $oldRoleExists = az role assignment list --assignee $principalId --scope $resourceId --role $oldRoleName --query "[].id" -o tsv
        if (-not $oldRoleExists) {
            Write-Host "Old role assignment for '$oldRoleName' role does not exist on resource"
        }
        else {
            Write-Host "Deleting old role assignment for '$oldRoleName' role"
            az role assignment delete --assignee $principalId --scope $resourceId --role $oldRoleName
            $oldRoleExists = az role assignment list --assignee $principalId --scope $resourceId --role $oldRoleName --query "[].id" -o tsv
            if ($oldRoleExists) {
                throw "Error: Failed to delete old role assignment"
            }
            Write-Host "Old role assignment deleted successfully"
        }
    }
    
  3. 运行函数 Update-ImmersiveReaderRoleAssignment,根据需要将 <PARAMETER_VALUES> 占位符替换为你自己的值。

    Update-ImmersiveReaderRoleAssignment -SubscriptionName '<SUBSCRIPTION_NAME>' -ResourceGroupName '<RESOURCE_GROUP_NAME>' -ResourceName '<RESOURCE_NAME>' -AADAppIdentifierUri '<MICROSOFT_ENTRA_APP_IDENTIFIER_URI>'
    

    完整命令如下所示。 为了清楚起见,我们在此处将每个参数都置于其自己的行上,以便你可以查看整个命令。 请勿按原样复制或使用此命令。 复制并使用加上自己值的命令。 此示例为 <PARAMETER_VALUES> 提供了虚拟值。 你的值将有所不同,因为你为这些值提出你自己的名称。

    Update-ImmersiveReaderRoleAssignment
        -SubscriptionName 'MyOrganizationSubscriptionName'
        -ResourceGroupName 'MyResourceGroupName'
        -ResourceName 'MyOrganizationImmersiveReader'
        -AADAppIdentifierUri 'https://MyOrganizationImmersiveReaderAADApp'
    
    参数 注释
    SubscriptionName Azure 订阅的名称。
    ResourceGroupName 包含沉浸式阅读器资源的资源组的名称。
    ResourceName 沉浸式阅读器资源的名称。
    AADAppIdentifierUri Microsoft Entra 应用的 URI。

下一步