セキュリティ アドバイザリ - Microsoft Entra 認証のロールの割り当てを更新する

Immersive Reader の Microsoft Entra 認証に影響を与えるセキュリティ バグが検出されました。 Immersive Reader リソースに対するアクセス許可を変更することをお勧めします。

背景

Immersive Reader リソースを最初に作成し、それらを Microsoft Entra 認証用に構成する際、Immersive Reader リソースにアクセスするためのアクセス許可を Microsoft Entra アプリケーション ID に付与する必要があります。 これは、"ロールの割り当て" と呼ばれます。 以前アクセス許可に使用されていた Azure ロールは、Cognitive Services ユーザー ロールでした。

セキュリティ監査中、この Cognitive Services ユーザー ロールに、キーを一覧表示するためのアクセス許可が含まれていることが検出されました。 Immersive Reader の統合では、クライアント Web アプリとブラウザーで、この Microsoft Entra アクセス トークンを使用する必要があるため、これは多少問題になります。 アクセス トークンが不正なアクターや攻撃者によって盗まれた場合、このアクセス トークンが Immersive Reader リソースの list keys に使用されるおそれがあるという懸念があります。 攻撃者がお使いのリソースの list keys を行うと、リソースの Subscription Key が取得されてしまいます。 リソースの Subscription Key は認証メカニズムとして使用され、シークレットと見なされます。 攻撃者は、リソースの Subscription Key を取得すると、Immersive Reader リソース エンドポイントに対して有効で認証された API 呼び出しを行うことができるようになります。これにより、エンドポイントでの使用量とスロットルが増加するため、サービス拒否を引き起こす危険性があります。 また、お使いの Immersive Reader リソースを不正に使用できるようになるため、請求料金が増える可能性もあります。

ただし、実際には、この攻撃や悪用が発生する可能性は低く、不可能な場合さえあります。 Immersive Reader リソースの場合、お客様は、https://cognitiveservices.azure.com の対象ユーザーを使用して Microsoft Entra アクセス トークンを取得します。 リソースの list keys を正常に実行するには、Microsoft Entra アクセス トークンに https://management.azure.com の対象ユーザーが含まれている必要があります。 Immersive Reader のシナリオに使用されるアクセス トークンには、必要な対象ユーザーが含まれていないために list keys では機能しないため、一般的には、これはそれほど大きな問題にはなりません。 アクセス トークンの対象ユーザーを変更するには、攻撃者は、トークンを取得するために Microsoft Entra ID に対して呼び出しを行う前に、トークン取得コードをハイジャックして対象ユーザーを変更する必要があります。 Microsoft では、Immersive Reader 認証のベスト プラクティスとして、クライアントまたはブラウザー内ではなく Web アプリケーションのバックエンドで Microsoft Entra アクセス トークンを作成することをお客様にお勧めしているため、この場合も、アクセス トークンが悪用されるおそれはほとんどありません。 このような場合、トークンの取得はバックエンド サービスで発生するため、攻撃者がそのプロセスを侵害して対象ユーザーを変更する可能性は低く、おそらく攻撃することさえできないでしょう。

本当に懸念されるのは、お客様がクライアント コードで直接 Microsoft Entra からトークンを取得する場合です。 これを回避することを強くお勧めします。ただし、お客様は適切と思われる場合に自由に実装できるため、一部のお客様がこれを行っている可能性があります。

Microsoft Entra アクセス トークンを使用して list keysが行われる可能性があるという懸念を軽減するために、Microsoft では、list keys を実行するためのアクセス許可を持たない Cognitive Services Immersive Reader User と呼ばれる新しい組み込みの Azure ロールを作成しました。 この新しいロールは、Cognitive Services User ロールとは異なり、Azure AI サービス プラットフォーム用の共有ロールではなく、 Immersive Reader 固有であり、Immersive Reader API に対する呼び出しのみが許可されます。

すべてのお客様に対して、元の Cognitive Services User ロールの代わりに、この新しい Cognitive Services Immersive Reader User ロールを使用することをお勧めします。 ロールの割り当てのアクセス許可を切り替えるためにお使いの各リソースで実行できるスクリプトを次に示します。

実装シナリオや攻撃の可能性に関係なく、すべてのユーザーに対してこの脆弱性のパッチが確実に適用されるようにするために、この推奨事項はすべてのお客様に適用されます。

これを適用しなくても、中断されるものはありません。 古いロールは引き続き機能します。 ほとんどのお客様にとって、セキュリティへの影響は最小限に抑えられます。 ただし、前述のセキュリティ上の懸念を緩和するために、新しいロールに移行することをお勧めします。 この更新プログラムの適用は、セキュリティ アドバイザリの推奨事項であり、必須ではありません。

操作方法: Immersive Reader リソースを作成する」で提供しているスクリプトを使用して新しく作成する Immersive Reader リソースでは、この新しいロールが自動的に使用されます。

ロールを更新し、サブスクリプション キーをローテーションする

2022 年 2 月より前に、「操作方法: Immersive Reader リソースを作成する」で提供しているスクリプトを使用して Immersive Reader リソースを作成および構成した場合は、次の操作を行って、お使いのすべての Immersive Reader リソースに対するロールの割り当てのアクセス許可を更新することをお勧めします。 この操作では、1 つのリソースに対するロールの割り当てを更新するためのスクリプトを実行します。 複数のリソースがある場合、このスクリプトを複数回 (各リソースに 1 回ずつ) 実行します。

次のスクリプトを使用してロールを更新した後、リソースのサブスクリプション キーをローテーションすることもお勧めします。 これは、万一、キーが悪用によって侵害され、誰かが実際にユーザーの同意なしにサブスクリプション キー認証でユーザーのリソースを使用している場合、それに備えるためです。 キーをローテーションすると、以前のキーは無効になり、それ以降のアクセスは拒否されます。 Microsoft Entra 認証を使用しているお客様 (現在の Immersive Reader SDK 実装を使用しているすべてのお客様) の場合、認証にはサブスクリプション キーではなく、Microsoft Entra アクセス トークンが使用されるため、キーをローテーションしても Immersive Reader サービスには影響しません。 サブスクリプション キーのローテーションは、もう 1 つの予防措置にすぎません。

サブスクリプション キーのローテーションは、Azure portal で行うことができます。 リソースに移動し、Keys and Endpoint セクションに移動します。 上部に、Regenerate Key1Regenerate Key2 へのボタンがあります。

Screenshot of the Azure portal showing an Immersive Reader resource with the Keys and Endpoint section selected, which shows the Regenerate Keys buttons at the top.

Azure PowerShell を使用してロールの割り当てを更新する

  1. まず、Azure Cloud Shell を開きます。 左上のドロップダウンを参照するか、「pwsh」と入力して、Cloud Shell が PowerShell に設定されていることを確認します。

  2. 以下のコード スニペットをコピーしてシェルに貼り付けます。

    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. <PARAMETER_VALUES> プレースホルダーを独自の値に適宜置き換えて、関数 Update-ImmersiveReaderRoleAssignment を実行します。

    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 お使いの Immersive Reader リソースが含まれているリソース グループの名前。
    ResourceName お使いの Immersive Reader リソースの名前。
    AADAppIdentifierUri Microsoft Entra アプリの URI。

次のステップ