Data Exposure on Azure
How Data Exposure Manifests in Azure
Data exposure in Azure APIs often stems from misconfigured Azure services, improper handling of sensitive data in Azure-specific contexts, and overlooked security settings in Azure's managed services. Understanding these Azure-specific manifestations is crucial for preventing data breaches.
One common pattern involves Azure Key Vault secrets inadvertently exposed through API responses. Developers sometimes cache or log Key Vault responses without proper sanitization, creating a direct path for credential theft. For example:
// Vulnerable: Exposing Key Vault secrets in API response
[HttpGet("secrets/{name}")]
public async Task<ActionResult> GetSecret(string name)
{
var secret = await _keyVaultClient.GetSecretAsync(name);
return Ok(new { Secret = secret.Value }); // Direct exposure!
}
Another Azure-specific scenario involves Azure Storage SAS (Shared Access Signature) tokens being mishandled. SAS tokens grant granular access to storage resources, and when embedded in API responses or client-side code, they become attack vectors:
// Vulnerable: Exposing SAS tokens to clients
const blobService = AzureStorage.createBlobService();
const sasToken = blobService.generateSharedAccessSignature(
'mycontainer',
'myfile.txt',
{ permissions: 'r', expiry: new Date(Date.now() + 3600000) }
);
return { url: blobService.getUrl('mycontainer', 'myfile.txt', sasToken) };
Azure Functions present unique data exposure risks when using the req object. Developers often log request bodies containing sensitive data without filtering:
// Vulnerable: Logging sensitive data in Azure Functions
public static async Task<HttpResponseMessage> Run(
HttpRequestMessage req,
TraceWriter log)
{
var body = await req.Content.ReadAsStringAsync();
log.Info($"Request body: {body}"); // Could contain PII, tokens, etc.
return req.CreateResponse(HttpStatusCode.OK);
}
Azure API Management exposes another attack surface. Policies that log request and response bodies without redaction can capture sensitive data:
<!-- Vulnerable: Logging sensitive data in API Management policies -->
<log-to-eventhub>
<message>
<forwarded-for/>
<method/>
<url/>
<status-code/>
<response-content>@{(string)context.Variables["message"]}</response-content>
</message>
</log-to-eventhub>
Azure's Managed Identity system, while secure by default, can lead to data exposure when credentials are improperly scoped or when identity tokens are exposed in API responses:
# Vulnerable: Exposing identity tokens
@app.route('/auth-token')
def get_auth_token():
credentials = DefaultAzureCredential()
token = credentials.get_token('https://management.azure.com/.default')
return {'token': token.token} # Direct exposure of access token
Azure-Specific Detection
Detecting data exposure in Azure APIs requires both manual code review and automated scanning that understands Azure's unique patterns. middleBrick's Azure-aware scanning identifies these specific vulnerabilities without requiring credentials or access to your Azure subscription.
middleBrick tests for Azure-specific data exposure patterns including:
- Key Vault secret exposure in API responses
- SAS token leakage in client responses
- Azure Functions log data containing sensitive information
- API Management policies that log sensitive data
- Managed Identity token exposure
- Azure Storage connection strings in responses
- Azure AD token leakage
The scanner actively probes your API endpoints for these patterns using a black-box approach. For Key Vault testing, middleBrick sends requests to endpoints that might interact with Azure Key Vault and analyzes responses for secret patterns:
{
"category": "Data Exposure",
"severity": "High",
"finding": "Azure Key Vault secret exposure detected",
"description": "API endpoint /api/secrets/{name} returns raw Key Vault secret values without authorization checks.",
"remediation": "Implement proper authorization checks and return only metadata about secrets, not their values."
}
For SAS token detection, middleBrick analyzes response bodies for Azure Storage SAS token patterns and tests whether tokens are properly scoped:
{
"category": "Data Exposure",
"severity": "Medium",
"finding": "SAS token with excessive permissions detected",
"description": "SAS token returned to client includes write permissions (sw) when only read access was needed.",
"remediation": "Scope SAS tokens to minimum required permissions and implement short expiry times."
}
middleBrick also detects Azure-specific logging vulnerabilities by analyzing API responses and error messages for patterns that suggest sensitive data logging:
{
"category": "Data Exposure",
"severity": "High",
"finding": "Azure Functions logging vulnerability",
"description": "API logs request bodies without sanitization, potentially exposing PII and authentication tokens.",
"remediation": "Implement request body filtering and log only non-sensitive metadata."
}
The scanner's OpenAPI analysis complements runtime testing by examining your API specification for Azure-specific security annotations and identifying endpoints that should be protected but lack proper security schemes:
| Azure Service | Common Exposure Pattern | Detection Method |
|---|---|---|
| Azure Key Vault | Secret value exposure | Response pattern matching |
| Azure Storage | SAS token leakage | SAS token regex scanning |
| Azure Functions | Log data exposure | Error message analysis |
| API Management | Policy logging | Response content analysis |
| Managed Identity | Token exposure | Token pattern detection |
Azure-Specific Remediation
Remediating data exposure in Azure APIs requires leveraging Azure's native security features and following Azure-specific best practices. Here are code examples for common Azure data exposure scenarios:
For Key Vault secrets, never return raw secret values. Instead, use Azure's built-in authorization and return only metadata:
[HttpGet("secrets/{name}")]
public async Task<ActionResult> GetSecretMetadata(string name)
{
// Check if caller has permission to access this secret
var authResult = await _keyVaultClient.CheckAccessPolicyAsync(name);
if (!authResult.HasAccess)
{
return Forbid();
}
// Return metadata only, not the secret value
var secretProperties = await _keyVaultClient.GetSecretPropertiesAsync(name);
return Ok(new
{
Name = secretProperties.Name,
Created = secretProperties.Created,
Updated = secretProperties.Updated,
RecoveryLevel = secretProperties.RecoveryLevel
});
}
For SAS tokens, implement proper scoping and use Azure's token utilities to avoid exposure:
public static class SasTokenHelper
{
public static string GenerateSecureSasToken(string containerName, string blobName)
{
// Create a policy with minimum required permissions
var sasConstraints = new SharedAccessBlobPolicy
{
SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-15),
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(30),
Permissions = SharedAccessBlobPermissions.Read
};
// Generate token without exposing it to clients
var blobService = new BlobServiceClient(connectionString);
var containerClient = blobService.GetBlobContainerClient(containerName);
var blobClient = containerClient.GetBlobClient(blobName);
// Return only the signed URI, not the token itself
return blobClient.GenerateUri(BlobSasBuilder.Parse(sasConstraints));
}
}
For Azure Functions, implement request body filtering before logging:
public static class LoggingHelper
{
private static readonly string[] SensitiveFields =
{ "password", "token", "key", "secret", "creditcard" };
public static string SanitizeRequestBody(string body)
{
if (string.IsNullOrEmpty(body)) return body;
try
{
var json = JObject.Parse(body);
foreach (var field in SensitiveFields)
{
if (json.SelectToken(field) != null)
{
json.SelectToken(field).Replace("REDACTED");
}
}
return json.ToString();
}
catch
{
// If not JSON, attempt to redact common patterns
return Regex.Replace(body,
@"(password|token|key|secret)=[^&\s]+",
"$1=REDACTED",
RegexOptions.IgnoreCase);
}
}
}
// In your function
public static async Task<HttpResponseMessage> Run(
HttpRequestMessage req,
TraceWriter log)
{
var body = await req.Content.ReadAsStringAsync();
var sanitizedBody = LoggingHelper.SanitizeRequestBody(body);
log.Info($"Request body: {sanitizedBody}");
return req.CreateResponse(HttpStatusCode.OK);
}
For API Management, use Azure's built-in policy features to redact sensitive data:
<!-- Redact sensitive data in API Management -->
<log-to-eventhub>
<message>
<forwarded-for/>
<method/>
<url/>
<status-code/>
<response-content>
@{
var content = (string)context.Variables["message"];
// Redact common sensitive patterns
content = Regex.Replace(content,
@"(password|token|key|secret)=[^&\s]+",
"$1=REDACTED",
RegexOptions.IgnoreCase);
return content;
}</response-content>
</message>
</log-to-eventhub>
For Managed Identity scenarios, use Azure's client libraries that handle tokens securely without exposing them:
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
# Secure pattern: token never exposed to application code
credential = DefaultAzureCredential()
secret_client = SecretClient(vault_url=VAULT_URL, credential=credential)
# The token is handled internally by the SDK
secret = secret_client.get_secret("my-secret")
Azure also provides Data Protection APIs for encrypting sensitive data at rest and in transit. Use these instead of custom encryption:
using Microsoft.AspNetCore.DataProtection;
public class SecureDataService
{
private readonly IDataProtector _protector;
public SecureDataService(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("MySecureData");
}
public string ProtectSensitiveData(string sensitiveData)
{
return _protector.Protect(sensitiveData);
}
public string UnprotectSensitiveData(string protectedData)
{
return _protector.Unprotect(protectedData);
}
}
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |