Insecure Direct Object Reference on Azure
How Insecure Direct Object Reference Manifests in Azure
Insecure Direct Object Reference (IDOR) in Azure environments typically occurs when applications expose internal identifiers that Azure services use to reference resources, and these identifiers are not properly validated before granting access. Azure's architecture, with its resource management APIs and identity systems, creates several unique attack vectors for IDOR vulnerabilities.
One common pattern involves Azure Resource Manager (ARM) template deployments where resource IDs are passed as parameters. Consider this vulnerable Azure Function code:
[FunctionName("GetStorageAccount")]
public static async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
string storageAccountName)
{
var storageAccount = await CloudStorageAccount.Parse(
$"DefaultEndpointsProtocol=https;AccountName={storageAccountName};AccountKey=...");
// No validation that the caller owns this storage account
var blobs = await storageAccount.CreateCloudBlobClient()
.GetContainerReference("$logs")
.ListBlobsSegmentedAsync(null);
return req.CreateResponse(HttpStatusCode.OK, blobs);
}
An attacker can enumerate storage account names and access logs from any account in the subscription. This is particularly dangerous in multi-tenant Azure environments where customers share infrastructure.
Another Azure-specific IDOR pattern occurs with Azure Key Vault secrets. Developers often create endpoints that retrieve secrets by name:
[FunctionName("GetSecret")]
public static async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
string secretName)
{
var keyVaultClient = new SecretClient(
new Uri($"https://mykeyvault.vault.azure.net"),
new DefaultAzureCredential());
var secret = await keyVaultClient.GetSecretAsync(secretName);
// No check if caller should access this specific secret
return req.CreateResponse(HttpStatusCode.OK, secret);
}
If the Key Vault access policy grants the function app broad permissions but the application logic doesn't verify ownership, attackers can enumerate secrets across tenants or subscriptions.
Azure Logic Apps present another attack surface. When Logic Apps expose HTTP endpoints that accept resource IDs:
{
"type": "Request",
"recurrence": {
"frequency": "Minute",
"interval": 1
},
"inputs": {
"method": "GET",
"uri": "@uri"
}
}
Without proper validation, these workflows can be manipulated to access resources belonging to other users or subscriptions.
Azure-Specific Detection
Detecting IDOR vulnerabilities in Azure requires understanding both Azure's resource model and the specific patterns attackers use to exploit these flaws. middleBrick's Azure-specific scanning includes several unique checks:
ARM Template Resource Enumeration - The scanner attempts to access ARM resources using predictable naming patterns and common identifiers. For storage accounts, it tries variations like logs, data, and backup containers without proper authentication.
Key Vault Secret Enumeration - middleBrick tests for secret enumeration by attempting to access common secret names (ConnectionString, ApiKey, Password) across different vaults. It also checks if the endpoint returns different error messages for existing vs. non-existing secrets, which can leak information.
Managed Identity Abuse - Azure's managed identities can be a double-edged sword. If an API accepts a resource ID and uses the app's managed identity to access it, IDOR becomes trivial. middleBrick tests whether the endpoint properly validates that the caller has permission to access the specific resource being requested.
Cross-Subscription Access - In Azure environments with multiple subscriptions, middleBrick tests whether APIs properly scope access to the caller's subscription. It attempts to access resources using IDs from different subscriptions to verify proper isolation.
Here's how you might manually test for IDOR in Azure Functions:
# Test for storage account IDOR
for account in storage1 storage2 storage3; do
curl -s "https://yourapp.azurewebsites.net/api/storage/$account/logs" | jq '.items | length'
done
# Test for Key Vault IDOR
for secret in ConnectionString ApiKey Password; do
curl -s "https://yourapp.azurewebsites.net/api/secret/$secret" | jq '.value'
done
middleBrick CLI Integration - You can scan Azure endpoints directly from your terminal:
npm install -g middlebrick
middlebrick scan https://yourapp.azurewebsites.net/api/storage --azure-specific
The Azure-specific scan mode enables additional checks for ARM resource patterns, Key Vault access controls, and managed identity abuse scenarios that generic API scanners miss.
Azure-Specific Remediation
Remediating IDOR in Azure requires implementing proper authorization checks at the resource level, not just at the endpoint level. Here are Azure-specific patterns for secure implementation:
Resource-Based Authorization with Azure RBAC - Instead of accepting raw resource IDs, use Azure's built-in authorization:
[FunctionName("GetStorageAccount")]
public static async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
string storageAccountName)
{
var credential = new DefaultAzureCredential();
var armClient = new ArmClient(credential);
// Get the caller's identity and verify they have access
var resource = armClient.GetResource($"/subscriptions/{subscriptionId}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}");
if (!await resource.Data.ManagedBy.ContainsAsync("me")) {
return req.CreateResponse(HttpStatusCode.Forbidden);
}
// Now it's safe to access the storage account
var storageAccount = new CloudStorageAccount(
new StorageCredentials(storageAccountName, "..."));
var blobs = await storageAccount.CreateCloudBlobClient()
.GetContainerReference("$logs")
.ListBlobsSegmentedAsync(null);
return req.CreateResponse(HttpStatusCode.OK, blobs);
}
Service Principal Token Validation - For Key Vault access, validate that the caller has explicit permissions:
[FunctionName("GetSecret")]
public static async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
string secretName)
{
var credential = new DefaultAzureCredential();
var keyVaultClient = new SecretClient(
new Uri("https://mykeyvault.vault.azure.net"),
credential);
// Check if the caller has access to this specific secret
var accessPolicy = await keyVaultClient.GetAccessPolicyAsync();
var hasAccess = accessPolicy.Any(policy =>
policy.ObjectId == credential.GetAccountId() &&
policy.Permissions.Secrets.Contains(secretName));
if (!hasAccess) {
return req.CreateResponse(HttpStatusCode.Forbidden);
}
var secret = await keyVaultClient.GetSecretAsync(secretName);
return req.CreateResponse(HttpStatusCode.OK, secret);
}
Azure API Management Policies - Implement IDOR protection at the gateway level:
<policies>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" />
<backend>
<forward-request />
</backend>
<on-error>
<set-header name="X-Resource-Access" exists-action="override">
<value>@{
var resourceId = context.Variables["resourceId"];
var identity = context.User.Identity;
// Custom logic to validate resource access
if (!HasAccessToResource(identity, resourceId)) {
context.Response.StatusCode = 403;
return "Forbidden";
}
return "Allowed";
}</value>
</set-header>
</on-error>
</policies>
Azure Functions Authorization Level - Use the appropriate authorization levels and supplement with custom validation:
[FunctionName("SecureResourceAccess")]
[Authorize(Policy = "ResourceOwnerOnly")] // Custom policy
public static async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
string resourceId)
{
// The Authorize attribute handles basic auth, but we still need resource-level checks
var resource = await GetResourceById(resourceId);
if (resource.OwnerId != GetCallerUserId()) {
return req.CreateResponse(HttpStatusCode.Forbidden);
}
return req.CreateResponse(HttpStatusCode.OK, await ProcessResource(resource));
}Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |