HIGH bola idorazure

Bola Idor on Azure

How BOLA/IdOR Manifests in Azure

Broken Object Level Authorization (BOLA), also known as Insecure Direct Object References (IDOR), occurs when an application fails to properly verify whether a user has permission to access a specific object. In Azure environments, this vulnerability manifests through several Azure-specific patterns and APIs.

One common Azure-specific manifestation occurs in Azure Resource Manager (ARM) APIs. When developers use ARM templates or SDKs to access resources, they often rely on user-supplied identifiers without proper authorization checks. For example:

// Vulnerable Azure Function code
const { DefaultAzureCredential } = require("@azure/identity");
const { ResourceManagementClient } = require("@azure/arm-resources");

const credential = new DefaultAzureCredential();
const client = new ResourceManagementClient(credential, process.env.AZURE_SUBSCRIPTION_ID);

// Vulnerable: No authorization check
module.exports = async function (context, req) {
    const resourceId = req.query.resourceId; // User-controlled input
    const resource = await client.resources.get(resourceId);
    context.res = { body: resource };
}

In this Azure Function, an attacker can modify the resourceId parameter to access any resource in the subscription, including those belonging to other tenants or resources they shouldn't see.

Another Azure-specific pattern appears in Azure Active Directory (AAD) Graph API calls. When applications fetch user data or group memberships without proper scope validation:

// Vulnerable AAD Graph API usage
const { GraphServiceClient } = require("@azure/msal-node");

const client = new GraphServiceClient(config);

// Vulnerable: No check if user can access this userId
module.exports = async function (context, req) {
    const userId = req.query.userId; // User-controlled
    const user = await client.api(`users/${userId}`).get();
    context.res = { body: user };
}

Azure Storage services also present unique BOLA opportunities. When SAS (Shared Access Signature) tokens are generated without proper scoping, attackers can manipulate resource paths:

// Vulnerable SAS token generation
const { BlobServiceClient } = require("@azure/storage-blob");

const blobServiceClient = new BlobServiceClient(process.env.AZURE_STORAGE_CONNECTION_STRING);

// Vulnerable: No validation of container/blob access
module.exports = async function (context, req) {
    const containerName = req.query.container; // User-controlled
    const blobName = req.query.blob; // User-controlled
    
    const containerClient = blobServiceClient.getContainerClient(containerName);
    const blobClient = containerClient.getBlobClient(blobName);
    
    const token = await blobClient.generateBlobSASQueryParameters({
        expiresOn: new Date(Date.now() + 3600000),
        containerName: containerName,
        blobName: blobName,
        permissions: "r"
    });
    
    context.res = { body: token.url }
}

Azure Key Vault presents another attack surface. Applications that accept vault names or secret keys from users without validating access rights:

// Vulnerable Key Vault access
const { DefaultAzureCredential } = require("@azure/identity");
const { KeyVaultSecretClient } = require("@azure/keyvault-secrets");

const credential = new DefaultAzureCredential();

// Vulnerable: No check if user can access this vault
module.exports = async function (context, req) {
    const vaultUrl = req.query.vaultUrl; // User-controlled
    const secretName = req.query.secretName; // User-controlled
    
    const client = new KeyVaultSecretClient(vaultUrl, credential);
    const secret = await client.getSecret(secretName);
    context.res = { body: secret };
}

Azure Logic Apps workflows can also be vulnerable when they accept parameters that map directly to resource identifiers without validation:

// Vulnerable Logic App HTTP trigger
module.exports = async function (context, req) {
    const resourceId = req.query.resourceId;
    
    // Direct ARM call without authorization check
    const response = await fetch(`https://management.azure.com/${resourceId}?api-version=2021-01-01`, {
        headers: { "Authorization": `Bearer ${process.env.ACCESS_TOKEN}` }
    });
    
    context.res = { body: await response.json() };
}

These Azure-specific patterns highlight how BOLA vulnerabilities emerge when developers trust user input for resource identifiers without validating permissions at the object level.

Azure-Specific Detection

Detecting BOLA vulnerabilities in Azure environments requires both static analysis and dynamic testing. Azure's RBAC (Role-Based Access Control) system provides some protection, but misconfigurations are common.

For manual detection, start by examining your Azure Function and Logic App code for patterns where resource identifiers are accepted as parameters:

// Search for these patterns in your codebase
const resourceId = req.query.resourceId;
const userId = req.query.userId;
const vaultUrl = req.query.vaultUrl;
const containerName = req.query.container;

Look for API endpoints that accept these parameters and then directly use them in Azure SDK calls without authorization checks.

Azure Resource Graph queries can help identify publicly accessible resources that might be vulnerable:

// Azure CLI query to find publicly accessible resources
az graph query -q "
    Resources 
    | where type == 'microsoft.web/sites' 
    | where properties.enabled == true 
    | where properties.clientAffinityEnabled == true
"

For automated detection, middleBrick's Azure-specific scanning identifies BOLA vulnerabilities by testing authenticated and unauthenticated access patterns. The scanner tests for:

Test TypeAzure-Specific TargetDetection Method
Resource AccessARM APIsParameter tampering with valid tokens
Storage AccessBlob/Queue StorageSAS token manipulation
Key VaultSecrets/KeysVault URL parameter testing
AAD GraphUser/Group dataObject ID manipulation

middleBrick's Azure scanning specifically tests for these patterns by submitting modified resource identifiers while maintaining valid authentication tokens, revealing whether the backend properly validates object-level permissions.

Azure Security Center and Defender for Cloud can also help detect some BOLA patterns through their continuous monitoring:

// Enable Azure Defender for App Service to detect BOLA
az security pricing create --name "AppServers" --tier "standard"

The Azure Monitor logs can be analyzed for suspicious access patterns:

// Log Analytics query for suspicious resource access
AzureActivity
| where OperationNameValue contains "Microsoft.Resources"
| where CallerIpAddress != "" 
| summarize count() by Caller, ResourceId, bin(TimeGenerated, 1h)
| where count_ > 10 // Unusual access pattern

For comprehensive Azure BOLA detection, combine these approaches with middleBrick's automated scanning, which tests the actual attack surface by attempting to access resources across different permission contexts.

Azure-Specific Remediation

Remediating BOLA vulnerabilities in Azure requires implementing proper authorization checks at the object level. Here are Azure-specific remediation patterns:

For ARM resource access, always validate that the user has explicit permission to the specific resource:

// Secure Azure Function with resource-level authorization
const { DefaultAzureCredential } = require("@azure/identity");
const { ResourceManagementClient } = require("@azure/arm-resources");

const credential = new DefaultAzureCredential();
const client = new ResourceManagementClient(credential, process.env.AZURE_SUBSCRIPTION_ID);

module.exports = async function (context, req) {
    const resourceId = req.query.resourceId;
    
    // Get current user's object ID
    const currentUser = await client.resources.get(resourceId);
    
    // Check if user has explicit access to this resource
    const hasAccess = await client.accessCheck.hasAccess({
        resourceId: resourceId,
        permission: "read"
    });
    
    if (!hasAccess) {
        context.res = { 
            status: 403, 
            body: { error: "Access denied to this resource" }
        };
        return;
    }
    
    const resource = await client.resources.get(resourceId);
    context.res = { body: resource };
}

For Azure Storage, implement strict SAS token policies and validate user permissions:

// Secure SAS token generation with user validation
const { BlobServiceClient } = require("@azure/storage-blob");
const { DefaultAzureCredential } = require("@azure/identity");

const blobServiceClient = new BlobServiceClient(process.env.AZURE_STORAGE_CONNECTION_STRING);

module.exports = async function (context, req) {
    const containerName = req.query.container;
    const blobName = req.query.blob;
    
    // Validate user has access to this container
    const credential = new DefaultAzureCredential();
    const hasAccess = await validateUserHasContainerAccess(credential, containerName);
    
    if (!hasAccess) {
        context.res = { status: 403, body: { error: "Access denied" } };
        return;
    }
    
    const containerClient = blobServiceClient.getContainerClient(containerName);
    const blobClient = containerClient.getBlobClient(blobName);
    
    // Generate SAS with specific permissions and time limits
    const token = await blobClient.generateBlobSASQueryParameters({
        expiresOn: new Date(Date.now() + 3600000),
        containerName: containerName,
        blobName: blobName,
        permissions: "r", // Only read
        ipRange: { start: "0.0.0.0", end: "255.255.255.255" }
    });
    
    context.res = { body: token.url };
}

async function validateUserHasContainerAccess(credential, containerName) {
    // Implement your own RBAC check here
    // This is a placeholder for actual permission validation
    return true;
}

For Azure Key Vault, use managed identities and explicit permission checks:

// Secure Key Vault access with managed identity
const { DefaultAzureCredential } = require("@azure/identity");
const { KeyVaultSecretClient } = require("@azure/keyvault-secrets");

module.exports = async function (context, req) {
    const secretName = req.query.secretName;
    
    // Use managed identity - no credentials in code
    const credential = new DefaultAzureCredential();
    const vaultUrl = process.env.KEY_VAULT_URL;
    
    // Check if user can access this specific secret
    const client = new KeyVaultSecretClient(vaultUrl, credential);
    
    try {
        // This will throw if user doesn't have access
        const secret = await client.getSecret(secretName);
        context.res = { body: secret };
    } catch (error) {
        if (error.statusCode === 403) {
            context.res = { 
                status: 403, 
                body: { error: "Access denied to this secret" }
            };
        } else {
            context.res = { 
                status: 500, 
                body: { error: "Internal server error" }
            };
        }
    }
}

For AAD Graph API calls, implement scope validation:

// Secure AAD Graph API with scope validation
const { GraphServiceClient } = require("@azure/msal-node");

module.exports = async function (context, req) {
    const userId = req.query.userId;
    
    // Validate that the user can access this userId
    const graphClient = new GraphServiceClient(config);
    
    // Check if current user can access target user
    const currentUser = await graphClient.api('/me').get();
    const canAccess = await canUserAccessUser(currentUser.id, userId);
    
    if (!canAccess) {
        context.res = { status: 403, body: { error: "Access denied" } };
        return;
    }
    
    const user = await graphClient.api(`users/${userId}`).get();
    context.res = { body: user };
}

async function canUserAccessUser(currentUserId, targetUserId) {
    // Implement your own access control logic
    // Check if users are in same group, same tenant, etc.
    return currentUserId === targetUserId; // Example: only allow self-access
}

Azure Policy can enforce BOLA prevention at the infrastructure level:

// Azure Policy to prevent public access to storage
{
  "policyDefinitions": [
    {
      "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0a780841-3d4e-477e-8a43-53c270f8d38a",
      "parameters": {}
    }
  ]
}

For Logic Apps, use Azure API Management to add authorization layers:

// Logic App with API Management protection
module.exports = async function (context, req) {
    const resourceId = req.query.resourceId;
    
    // Verify API Management subscription key
    const subscriptionKey = req.headers['Ocp-Apim-Subscription-Key'];
    if (!await isValidSubscriptionKey(subscriptionKey)) {
        context.res = { status: 401, body: { error: "Invalid subscription key" } };
        return;
    }
    
    // Then perform resource-level authorization
    const hasAccess = await checkResourceAccess(resourceId, subscriptionKey);
    if (!hasAccess) {
        context.res = { status: 403, body: { error: "Access denied" } };
        return;
    }
    
    // Process request
}

These Azure-specific remediation patterns ensure that object-level authorization is properly implemented, preventing BOLA vulnerabilities in your Azure applications.

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 BOLA vulnerabilities in Azure APIs?
middleBrick tests Azure APIs by submitting modified resource identifiers while maintaining valid authentication tokens. It attempts to access resources across different permission contexts to identify whether the backend properly validates object-level permissions. The scanner specifically tests ARM APIs, Azure Storage, Key Vault, and AAD Graph endpoints for parameter tampering vulnerabilities.
Can Azure's built-in RBAC prevent all BOLA vulnerabilities?
No, Azure's RBAC provides role-based access but doesn't prevent BOLA at the object level. Even with proper RBAC, if your application accepts resource identifiers from users without validating whether they have permission to access that specific object, BOLA vulnerabilities remain. You need both proper RBAC configuration AND object-level authorization checks in your application code.