Security Misconfiguration on Azure
How Security Misconfiguration Manifests in Azure
Security misconfiguration in Azure environments often stems from overly permissive settings that expose sensitive resources to unauthorized access. One common pattern involves Azure Storage Accounts configured with public access enabled on containers, allowing anyone on the internet to list and download files without authentication.
# Vulnerable configuration
az storage container set-permission --name backups --public-access container
This single command makes all files in the 'backups' container publicly readable, potentially exposing database backups, configuration files, or even application secrets.
Another frequent misconfiguration involves Azure Key Vault access policies that grant excessive permissions. Developers sometimes configure policies with 'all' permissions for convenience:
# Overly permissive Key Vault policy
az keyvault set-policy --name mykeyvault \
--object-id $USER_ID \
--secret-permissions all \
--key-permissions all \
--certificate-permissions all
Network security groups (NSGs) present another attack vector when configured too permissively. A common mistake is allowing all inbound traffic on specific ports:
# Insecure NSG rule
az network nsg rule create --resource-group myRG \
--nsg-name myNSG \
--name AllowAllHTTP \
--priority 100 \
--direction Inbound \
--access Allow \
--source-address-prefix '*' \
--source-port-range '*' \
--destination-address-prefix '*' \
--destination-port-range 80
Azure App Service misconfigurations frequently occur when application settings expose sensitive data. Environment variables containing connection strings or API keys might be logged or exposed through diagnostic settings:
// Insecure configuration in Azure App Service
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
// Default builder might expose sensitive data in logs
config.AddAzureAppConfiguration();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Azure SQL Database instances sometimes lack proper firewall rules or network isolation, allowing connections from any Azure service:
# Insecure firewall configuration
az sql server firewall-rule create --resource-group myRG \
--server mySQLServer \
--name AllowAllWindowsAzureIps \
--start-ip-address 0.0.0.0 \
--end-ip-address 0.0.0.0
Role-Based Access Control (RBAC) misconfigurations represent another critical area. Overly broad role assignments can grant users more privileges than necessary:
# Excessive RBAC permissions
az role assignment create --assignee $USER_EMAIL \
--role "Contributor" \
--resource-group myRG
The 'Contributor' role allows modification of all resources within the group, including deleting resources and changing permissions.
Azure-Specific Detection
Detecting security misconfigurations in Azure requires examining both infrastructure-as-code templates and live configurations. Azure Resource Manager (ARM) templates often reveal misconfigurations before deployment:
// Vulnerable ARM template snippet
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"properties": {
"supportsHttpsTrafficOnly": false, // Should be true
"networkAcls": {
"defaultAction": "Allow" // Should be "Deny"
}
}
}
Azure Policy provides built-in definitions for detecting common misconfigurations. For example, the 'Audit storage accounts open to all networks' policy identifies publicly accessible storage accounts:
# Check Azure Policy compliance
az policy state list --management-group myMG \
--filter "PolicyAssignmentId eq '/providers/Microsoft.Authorization/policyDefinitions/34c877bd-8e6d-482c-8d5f-6b64f6f70fc7'"
Azure Security Center continuously assesses configurations against security benchmarks. The Azure CIS benchmark includes specific checks for misconfigurations:
# View Security Center recommendations
az security adaptive-network-hardening list \
--resource-group myRG \
--resource-type Microsoft.Compute/virtualMachines \
--resource-name myVM
For API security specifically, middleBrick's Azure-focused scanning detects misconfigurations in Azure-hosted APIs by examining authentication mechanisms, authorization controls, and exposed endpoints without requiring credentials:
# Scan an Azure-hosted API endpoint
middlebrick scan https://myapi.azurewebsites.net/api/users
The scanner tests for common Azure-specific issues like improper CORS configurations, missing authentication on Azure Functions endpoints, and exposed Azure API Management endpoints.
Azure Monitor logs can reveal misconfigurations through failed authentication attempts and unusual access patterns:
// Query for suspicious storage account access
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.STORAGE"
and OperationName == "ListKeys"
and TimeGenerated > ago(24h)
| summarize count() by CallerIpAddress, bin(TimeGenerated, 1h)
Azure Key Vault audit logs help identify overly permissive access policies:
# Check Key Vault access patterns
az keyvault audit list --resource-group myRG --name mykeyvault \
--query "[?principalId != 'system']" --output table
Azure-Specific Remediation
Remediating Azure security misconfigurations requires leveraging Azure's native security features and following the principle of least privilege. For Azure Storage Accounts, the first step is disabling public access:
# Secure storage account configuration
az storage container set-permission --name sensitive-data \
--public-access off
# Enable HTTPS only
az storage account update --name myaccount \
--https-only true
Network security for storage accounts should use private endpoints instead of public IP access:
# Create private endpoint for storage
az network private-endpoint create --resource-group myRG \
--name myStoragePrivateEndpoint \
--vnet-name myVNet \
--subnet mySubnet \
--private-connection-resource-id \
"/subscriptions/$SUB_ID/resourceGroups/myRG/providers/Microsoft.Storage/storageAccounts/myaccount" \
--group-ids blob
Azure Key Vault access policies should follow the principle of least privilege:
# Secure Key Vault configuration
az keyvault set-policy --name mykeyvault \
--object-id $APP_ID \
--secret-permissions get list \
--key-permissions get \
--certificate-permissions get
Azure App Service configurations should use Managed Identities instead of hardcoded credentials:
# Enable Managed Identity
az webapp identity assign --resource-group myRG --name myapp
# Grant Key Vault access to the identity
az keyvault set-policy --name mykeyvault \
--object-id $(az webapp identity show --resource-group myRG \
--name myapp --query principalId -o tsv) \
--secret-permissions get
For Azure SQL Database, proper firewall rules should restrict access to specific IP ranges:
# Secure SQL Database firewall
az sql server firewall-rule create --resource-group myRG \
--server mySQLServer \
--name AllowMyOffice \
--start-ip-address 203.0.113.10 \
--end-ip-address 203.0.113.15
Virtual Network service endpoints provide additional security for Azure services:
# Enable service endpoint for Azure Storage
az network vnet subnet update --resource-group myRG \
--vnet-name myVNet \
--name mySubnet \
--service-endpoints Microsoft.Storage
Azure Policy can enforce secure configurations through deny policies:
// Deny policy for public storage access
{
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
"notEquals": true
}
]
},
"then": {
"effect": "deny"
}
}
}
Azure Security Center's secure score provides a roadmap for remediation:
# View Security Center recommendations
az security insight list --resource-group myRG \
--assessment-type VirtualMachines \
--query "[?severity == 'High']" --output table
Continuous monitoring with Azure Monitor alerts helps detect when configurations drift from secure baselines:
// Alert for public storage access
{
"name": "PublicStorageAccessAlert",
"description": "Alert when storage becomes publicly accessible",
"severity": 4,
"enabled": true,
"query": "AzureDiagnostics | where ResourceProvider == 'MICROSOFT.STORAGE' | where OperationName == 'ListKeys'",
"severityDescription": "Informational",
"queryType": "ResultCount",
"queryFrequency": "PT5M",
"queryPeriod": "PT5M",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
}