Proteggere un account Mappe di Azure con un token di firma di accesso condiviso

Questo articolo descrive come creare un account Mappe di Azure con un token di firma di accesso condiviso archiviato in modo sicuro che è possibile usare per chiamare l'API REST Mappe di Azure.

Prerequisiti

Scenario di esempio: Archiviazione sicura del token di firma di accesso condiviso

Una credenziale del token di firma di accesso condiviso concede il livello di accesso specificato a chiunque lo contenga, finché il token non scade o non viene revocato l'accesso. Le applicazioni che usano l'autenticazione con token di firma di accesso condiviso devono archiviare le chiavi in modo sicuro.

Questo scenario archivia in modo sicuro un token di firma di accesso condiviso come segreto in Key Vault e distribuisce il token in un client pubblico. Gli eventi del ciclo di vita dell'applicazione possono generare nuovi token di firma di accesso condiviso senza interrompere connessioni attive che usano token esistenti.

Per altre informazioni sulla configurazione di Key Vault, vedere la guida per gli sviluppatori di Azure Key Vault.

Lo scenario di esempio seguente usa due distribuzioni di modelli di Azure Resource Manager (ARM) per eseguire la procedura seguente:

  1. Creare un insieme di credenziali delle chiavi.
  2. Creare un'identità gestita assegnata dall'utente.
  3. Assegnare il controllo degli accessi in base al ruolo di Azure Mappe di Azure ruolo lettore dati all'identità gestita assegnata dall'utente.
  4. Creare un account Mappe di Azure con una configurazione CORS (Cross Origin Resource Sharing) e collegare l'identità gestita assegnata dall'utente.
  5. Creare e salvare un token di firma di accesso condiviso nell'insieme di credenziali delle chiavi di Azure.
  6. Recuperare il segreto del token di firma di accesso condiviso dall'insieme di credenziali delle chiavi.
  7. Creare una richiesta API REST Mappe di Azure che usa il token di firma di accesso condiviso.

Al termine, verranno visualizzati Mappe di Azure risultati dell'API Search Address (Non-Batch) REST in PowerShell con l'interfaccia della riga di comando di Azure. Le risorse di Azure vengono distribuite con autorizzazioni per connettersi all'account Mappe di Azure. Sono disponibili controlli per il limite massimo di velocità, le aree consentite, localhost i criteri CORS configurati e il controllo degli accessi in base al ruolo di Azure.

Distribuzione di risorse di Azure con l'interfaccia della riga di comando di Azure

I passaggi seguenti descrivono come creare e configurare un account Mappe di Azure con l'autenticazione con token di firma di accesso condiviso. In questo esempio l'interfaccia della riga di comando di Azure viene eseguita in un'istanza di PowerShell.

  1. Accedere alla sottoscrizione di Azure con az login.

  2. Registrare Key Vault, identità gestite e Mappe di Azure per la sottoscrizione.

    az provider register --namespace Microsoft.KeyVault
    az provider register --namespace Microsoft.ManagedIdentity
    az provider register --namespace Microsoft.Maps
    
  3. Recuperare l'ID oggetto Microsoft Entra.

    $id = $(az rest --method GET --url 'https://graph.microsoft.com/v1.0/me?$select=id' --headers 'Content-Type=application/json' --query "id")
    
  4. Creare un file modello denominato prereq.azuredeploy.json con il contenuto seguente:

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "location": {
                "type": "string",
                "defaultValue": "[resourceGroup().location]",
                "metadata": {
                    "description": "Specifies the location for all the resources."
                }
            },
            "keyVaultName": {
                "type": "string",
                "defaultValue": "[concat('vault', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "Specifies the name of the key vault."
                }
            },
            "userAssignedIdentityName": {
                "type": "string",
                "defaultValue": "[concat('identity', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "The name for your managed identity resource."
                }
            },
            "objectId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the object ID of a user, service principal, or security group in the Azure AD tenant for the vault. The object ID must be unique for the set of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets."
                }
            },
            "secretsPermissions": {
                "type": "array",
                "defaultValue": [
                    "list",
                    "get",
                    "set"
                ],
                "metadata": {
                    "description": "Specifies the permissions to secrets in the vault. Valid values are: all, get, list, set, delete, backup, restore, recover, and purge."
                }
            }
        },
        "resources": [
            {
                "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
                "name": "[parameters('userAssignedIdentityName')]",
                "apiVersion": "2018-11-30",
                "location": "[parameters('location')]"
            },
            {
                "apiVersion": "2021-04-01-preview",
                "type": "Microsoft.KeyVault/vaults",
                "name": "[parameters('keyVaultName')]",
                "location": "[parameters('location')]",
                "properties": {
                    "tenantId": "[subscription().tenantId]",
                    "sku": {
                        "name": "Standard",
                        "family": "A"
                    },
                    "enabledForTemplateDeployment": true,
                    "accessPolicies": [
                        {
                            "objectId": "[parameters('objectId')]",
                            "tenantId": "[subscription().tenantId]",
                            "permissions": {
                                "secrets": "[parameters('secretsPermissions')]"
                            }
                        }
                    ]
                }
            }
        ],
        "outputs": {
            "userIdentityResourceId": {
                "type": "string",
                "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]"
            },
            "userAssignedIdentityPrincipalId": {
                "type": "string",
                "value": "[reference(parameters('userAssignedIdentityName')).principalId]"
            },
            "keyVaultName": {
                "type": "string",
                "value": "[parameters('keyVaultName')]"
            }
        }
    }
    
    
  5. Distribuire le risorse dei prerequisiti create nel passaggio precedente. Specificare il proprio valore per <group-name>. Assicurarsi di usare lo stesso location account Mappe di Azure.

    az group create --name <group-name> --location "East US"
    $outputs = $(az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./prereq.azuredeploy.json" --parameters objectId=$id --query "[properties.outputs.keyVaultName.value, properties.outputs.userAssignedIdentityPrincipalId.value, properties.outputs.userIdentityResourceId.value]" --output tsv)
    
  6. Creare un file modello azuredeploy.json per effettuare il provisioning dell'account Mappe di Azure, dell'assegnazione di ruolo e del token di firma di accesso condiviso.

    Nota

    Ritiro del piano tariffario Gen1 di Mappe di Azure

    Il piano tariffario Gen1 è ora deprecato e verrà ritirato il 15/9/26. Il piano tariffario Gen2 sostituisce il piano tariffario Gen1 (S0 e S1). Se per l'account Mappe di Azure è selezionato il piano tariffario Gen1, è possibile passare al piano tariffario Gen2 prima che venga ritirato. In caso contrario, l'aggiornamento verrà eseguito automaticamente. Per altre informazioni, vedere Gestire il piano tariffario dell'account mappe di Azure.

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "location": {
                "type": "string",
                "defaultValue": "[resourceGroup().location]",
                "metadata": {
                    "description": "Specifies the location for all the resources."
                }
            },
            "keyVaultName": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId of the key vault."
                }
            },
            "accountName": {
                "type": "string",
                "defaultValue": "[concat('map', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "The name for your Azure Maps account."
                }
            },
            "userAssignedIdentityResourceId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId for the user assigned managed identity resource."
                }
            },
            "userAssignedIdentityPrincipalId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId for the user assigned managed identity resource."
                }
            },
            "pricingTier": {
                "type": "string",
                "allowedValues": [
                    "S0",
                    "S1",
                    "G2"
                ],
                "defaultValue": "G2",
                "metadata": {
                    "description": "The pricing tier for the account. Use S0 for small-scale development. Use S1 or G2 for large-scale applications."
                }
            },
            "kind": {
                "type": "string",
                "allowedValues": [
                    "Gen1",
                    "Gen2"
                ],
                "defaultValue": "Gen2",
                "metadata": {
                    "description": "The pricing tier for the account. Use Gen1 for small-scale development. Use Gen2 for large-scale applications."
                }
            },
            "guid": {
                "type": "string",
                "defaultValue": "[guid(resourceGroup().id)]",
                "metadata": {
                    "description": "Input string for new GUID associated with assigning built in role types."
                }
            },
            "startDateTime": {
                "type": "string",
                "defaultValue": "[utcNow('u')]",
                "metadata": {
                    "description": "Current Universal DateTime in ISO 8601 'u' format to use as the start of the SAS token."
                }
            },
            "duration" : {
                "type": "string",
                "defaultValue": "P1Y",
                "metadata": {
                    "description": "The duration of the SAS token. P1Y is maximum, ISO 8601 format is expected."
                }
            },
            "maxRatePerSecond": {
                "type": "int",
                "defaultValue": 500,
                "minValue": 1,
                "maxValue": 500,
                "metadata": {
                    "description": "The approximate maximum rate per second the SAS token can be used."
                }
            },
            "signingKey": {
                "type": "string",
                "defaultValue": "primaryKey",
                "allowedValues": [
                    "primaryKey",
                    "seconaryKey"
                ],
                "metadata": {
                    "description": "The specified signing key which will be used to create the SAS token."
                }
            },
            "allowedOrigins": {
                "type": "array",
                "defaultValue": [],
                "maxLength": 10,
                "metadata": {
                    "description": "The specified application's web host header origins (example: https://www.azure.com) which the Azure Maps account allows for CORS."
                }
            }, 
            "allowedRegions": {
                "type": "array",
                "defaultValue": [],
                "metadata": {
                    "description": "The specified SAS token allowed locations where the token may be used."
                }
            }
        },
        "variables": {
            "accountId": "[resourceId('Microsoft.Maps/accounts', parameters('accountName'))]",
            "Azure Maps Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '423170ca-a8f6-4b0f-8487-9e4eb8f49bfa')]",
            "sasParameters": {
                "signingKey": "[parameters('signingKey')]",
                "principalId": "[parameters('userAssignedIdentityPrincipalId')]",
                "maxRatePerSecond": "[parameters('maxRatePerSecond')]",
                "start": "[parameters('startDateTime')]",
                "expiry": "[dateTimeAdd(parameters('startDateTime'), parameters('duration'))]",
                "regions": "[parameters('allowedRegions')]"
            }
        },
        "resources": [
            {
                "name": "[parameters('accountName')]",
                "type": "Microsoft.Maps/accounts",
                "apiVersion": "2023-06-01",
                "location": "[parameters('location')]",
                "sku": {
                    "name": "[parameters('pricingTier')]"
                },
                "kind": "[parameters('kind')]",
                "properties": {
                    "cors": {
                        "corsRules": [
                            {
                                "allowedOrigins": "[parameters('allowedOrigins')]"
                            }
                        ]
                    }
                },
                "identity": {
                    "type": "UserAssigned",
                    "userAssignedIdentities": {
                        "[parameters('userAssignedIdentityResourceId')]": {}
                    }
                }
            },
            {
                "apiVersion": "2020-04-01-preview",
                "name": "[concat(parameters('accountName'), '/Microsoft.Authorization/', parameters('guid'))]",
                "type": "Microsoft.Maps/accounts/providers/roleAssignments",
                "dependsOn": [
                    "[parameters('accountName')]"
                ],
                "properties": {
                    "roleDefinitionId": "[variables('Azure Maps Data Reader')]",
                    "principalId": "[parameters('userAssignedIdentityPrincipalId')]",
                    "principalType": "ServicePrincipal"
                }
            },
            {
                "apiVersion": "2021-04-01-preview",
                "type": "Microsoft.KeyVault/vaults/secrets",
                "name": "[concat(parameters('keyVaultName'), '/', parameters('accountName'))]",
                "dependsOn": [
                    "[variables('accountId')]"
                ],
                "tags": {
                    "signingKey": "[variables('sasParameters').signingKey]",
                    "start" : "[variables('sasParameters').start]",
                    "expiry" : "[variables('sasParameters').expiry]"
                },
                "properties": {
                    "value": "[listSas(variables('accountId'), '2023-06-01', variables('sasParameters')).accountSasToken]"
                }
            }
        ]
    }
    
  7. Distribuire il modello con i parametri ID dall'insieme di credenziali delle chiavi e le risorse di identità gestite create nel passaggio precedente. Specificare il proprio valore per <group-name>. Quando si crea il token di firma di accesso condiviso, si imposta il allowedRegions parametro su eastus, westus2e westcentralus. È quindi possibile usare questi percorsi per effettuare richieste HTTP all'endpoint us.atlas.microsoft.com .

    Importante

    Salvare il token di firma di accesso condiviso nell'insieme di credenziali delle chiavi per impedire la visualizzazione delle credenziali nei log di distribuzione di Azure. Il segreto del token di firma di tags accesso condiviso contiene anche il nome della chiave di inizio, scadenza e firma, da visualizzare alla scadenza del token di firma di accesso condiviso.

     az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./azuredeploy.json" --parameters keyVaultName="$($outputs[0])" userAssignedIdentityPrincipalId="$($outputs[1])" userAssignedIdentityResourceId="$($outputs[2])" allowedOrigins="['http://localhost']" allowedRegions="['eastus', 'westus2', 'westcentralus']" maxRatePerSecond="10"
    
  8. Individuare e salvare una copia del segreto del token di firma di accesso condiviso singolo da Key Vault.

    $secretId = $(az keyvault secret list --vault-name $outputs[0] --query "[? contains(name,'map')].id" --output tsv)
    $sasToken = $(az keyvault secret show --id "$secretId" --query "value" --output tsv)
    
  9. Testare il token di firma di accesso condiviso effettuando una richiesta a un endpoint Mappe di Azure. In questo esempio viene specificato per assicurarsi che le route di richiesta vengano instradate all'area us.atlas.microsoft.com geografica degli Stati Uniti. Il token di firma di accesso condiviso consente aree all'interno dell'area geografica degli Stati Uniti.

    az rest --method GET --url 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052' --headers "Authorization=jwt-sas $($sasToken)" --query "results[].address"
    

Esempio di script completo

Per eseguire l'esempio completo, i file modello seguenti devono trovarsi nella stessa directory della sessione di PowerShell corrente:

  • prereq.azuredeploy.json creare l'insieme di credenziali delle chiavi e l'identità gestita.
  • azuredeploy.json creare l'account Mappe di Azure, configurare l'assegnazione di ruolo e l'identità gestita e archiviare il token di firma di accesso condiviso nell'insieme di credenziali delle chiavi.
az login
az provider register --namespace Microsoft.KeyVault
az provider register --namespace Microsoft.ManagedIdentity
az provider register --namespace Microsoft.Maps

$id = $(az rest --method GET --url 'https://graph.microsoft.com/v1.0/me?$select=id' --headers 'Content-Type=application/json' --query "id")
az group create --name <group-name> --location "East US"
$outputs = $(az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./prereq.azuredeploy.json" --parameters objectId=$id --query "[properties.outputs.keyVaultName.value, properties.outputs.userAssignedIdentityPrincipalId.value, properties.outputs.userIdentityResourceId.value]" --output tsv)
az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./azuredeploy.json" --parameters keyVaultName="$($outputs[0])" userAssignedIdentityPrincipalId="$($outputs[1])" userAssignedIdentityResourceId="$($outputs[2])" allowedOrigins="['http://localhost']" allowedRegions="['eastus', 'westus2', 'westcentralus']" maxRatePerSecond="10"
$secretId = $(az keyvault secret list --vault-name $outputs[0] --query "[? contains(name,'map')].id" --output tsv)
$sasToken = $(az keyvault secret show --id "$secretId" --query "value" --output tsv)

az rest --method GET --url 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052' --headers "Authorization=jwt-sas $($sasToken)" --query "results[].address"

Esempio reale

È possibile eseguire richieste per Mappe di Azure API dalla maggior parte dei client, ad esempio C#, Java o JavaScript. Le piattaforme di sviluppo di API come bruno o Postman possono convertire una richiesta API in un frammento di codice client di base in quasi qualsiasi linguaggio di programmazione o framework scelto. È possibile usare i frammenti di codice generati nelle applicazioni front-end.

L'esempio di codice JavaScript seguente illustra come usare il token di firma di accesso condiviso con l'API Di recupero JavaScript per ottenere e restituire Mappe di Azure informazioni. L'esempio usa l'API Get Search Address versione 1.0. Specificare il proprio valore per <your SAS token>.

Per il funzionamento di questo esempio, assicurarsi di eseguirlo dall'interno della stessa origine della allowedOrigins chiamata API. Ad esempio, se si specifica https://contoso.com come allowedOrigins nella chiamata API, la pagina HTML che ospita lo script JavaScript deve essere https://contoso.com.

async function getData(url = 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052') {
  const response = await fetch(url, {
    method: 'GET',
    mode: 'cors',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'jwt-sas <your SAS token>',
    }
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052')
  .then(data => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

Pulire le risorse

Quando le risorse di Azure non sono più necessarie, è possibile eliminarle:

az group delete --name {group-name}

Passaggi successivi

Distribuire un modello di Resource Manager di avvio rapido per creare un account Mappe di Azure che usa un token di firma di accesso condiviso:

Per esempi più dettagliati, vedere:

Trovare le metriche di utilizzo delle API per l'account Mappe di Azure:

Esplorare gli esempi che illustrano come integrare Microsoft Entra ID con Mappe di Azure: