HIGH insecure direct object referenceazure

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 IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

How does middleBrick detect IDOR vulnerabilities in Azure Functions specifically?
middleBrick tests Azure Functions by attempting to access resources using predictable identifiers and checking if the endpoint returns different responses based on resource ownership. It specifically looks for ARM resource patterns, Key Vault secret enumeration, and managed identity abuse scenarios that are unique to Azure's architecture.
Can IDOR vulnerabilities in Azure Logic Apps be detected automatically?