Unicode Normalization on Azure
How Unicode Normalization Manifests in Azure
Unicode normalization attacks in Azure environments often exploit the platform's global character handling and diverse authentication mechanisms. When users from different regions interact with Azure services, characters that appear identical to humans can have different underlying byte representations, creating security vulnerabilities.
A common Azure-specific manifestation occurs in Azure Active Directory authentication flows. Consider a scenario where an attacker registers an account with a username containing precomposed characters (like 'é' as a single code point) while the victim uses decomposed characters (like 'e' + combining acute accent). Azure's authentication system may treat these as distinct usernames, but certain legacy APIs or custom Azure Functions might normalize these differently, leading to authentication bypass.
// Vulnerable Azure Function example
const username = req.body.username;
const password = req.body.password;
// No normalization before database lookup
const user = await getUserFromDB(username);
if (user && user.password === password) {
return generateToken(user);
}This code fails to normalize the username before database lookup, allowing attackers to create accounts with visually identical but differently normalized usernames. Azure's global user base makes this particularly problematic, as users from different locales may naturally use different normalization forms.
Another Azure-specific scenario involves Azure Key Vault and secret management. When secrets contain Unicode characters, different normalization forms can lead to secret retrieval failures or, worse, secret collisions. An attacker could potentially register a secret using NFD form while the legitimate user uses NFC, causing Azure Key Vault to store them as separate entities.
Azure Storage services also exhibit Unicode normalization vulnerabilities. Blob names and container names are case-insensitive but not normalization-insensitive. An attacker could create a blob with NFKC-normalized characters that visually matches an existing blob in NFD form, leading to confusion and potential data exposure.
// Azure Blob Storage vulnerability example
const blobServiceClient = new BlobServiceClient(azureStorageConnectionString);
const containerClient = blobServiceClient.getContainerClient('documents');
// Attacker uploads with different normalization
const maliciousBlob = containerClient.getBlobClient('éxample.txt'); // NFC
await maliciousBlob.upload('malicious content', contentLength);
// User tries to access with different normalization
const legitimateBlob = containerClient.getBlobClient('éxample.txt'); // NFD
const content = await legitimateBlob.download(); // Gets different blob!
Azure API Management gateways can also be affected. When proxying requests to backend services, the gateway might normalize request parameters differently than the backend expects, creating authentication bypasses or data leakage opportunities.
Azure-Specific Detection
Detecting Unicode normalization vulnerabilities in Azure requires a multi-faceted approach that combines automated scanning with manual verification. middleBrick's Azure-specific scanning capabilities include tests for normalization-related issues across the platform's services.
For Azure Functions and App Services, middleBrick performs Unicode fuzzing by submitting requests with various normalization forms of the same apparent character. The scanner compares responses to identify inconsistencies that could indicate normalization vulnerabilities. This includes testing authentication endpoints, API routes, and file upload handlers.
# middleBrick CLI scan for Azure Functions
middlebrick scan https://myazurefunction.azurewebsites.net/api/
# Output includes normalization-specific findings
• Authentication Bypass: Different Unicode normalization forms accepted
• Secret Retrieval: Key Vault secret lookup inconsistent across normalization forms
• Blob Access: Storage container shows different behavior for visually identical names
# JSON output for CI/CD integration
{
"url": "https://myazurefunction.azurewebsites.net/api/",
"security_score": 72,
"normalization_vulnerabilities": [
{
"severity": "high",
"description": "Authentication endpoint accepts multiple Unicode normalization forms",
"remediation": "Normalize all input to NFC before processing"
}
]
}Azure Key Vault scanning with middleBrick specifically tests secret retrieval across different normalization forms. The scanner attempts to retrieve secrets using both NFC and NFD forms of characters that can be represented multiple ways, identifying cases where the same visual secret can be accessed through different byte sequences.
For Azure Storage services, middleBrick's scanner creates test blobs and containers using various normalization forms, then attempts to access them through different interfaces (REST API, SDK, Azure Portal) to identify inconsistencies. The scanner also tests case-insensitive and normalization-insensitive comparisons that Azure services might perform.
Azure API Management integration allows middleBrick to scan APIs exposed through the gateway, testing how the gateway normalizes requests before forwarding them to backend services. This is particularly important for APIs that handle international character sets or legacy systems with different Unicode handling expectations.
Azure-Specific Remediation
Remediating Unicode normalization vulnerabilities in Azure requires a systematic approach that leverages Azure's native capabilities while ensuring consistent character handling across all services. The primary strategy is to normalize all input to a single Unicode form before any processing occurs.
For Azure Functions and App Services, implement input normalization at the API boundary. Use Node.js's built-in unicodedata or the unorm library to normalize all incoming strings to NFC form before any business logic executes.
// Azure Function with proper normalization
const unorm = require('unorm');
module.exports = async function (context, req) {
// Normalize all string inputs to NFC
const normalizedUsername = unorm.nfc(req.body.username || '');
const normalizedPassword = unorm.nfc(req.body.password || '');
// Now perform authentication with normalized values
const user = await getUserFromDB(normalizedUsername);
if (user && user.password === normalizedPassword) {
return generateToken(user);
}
context.res = {
status: 401,
body: 'Unauthorized'
};
}
For Azure Key Vault integration, implement consistent normalization when storing and retrieving secrets. Create wrapper functions that always normalize secret names to NFC before Key Vault operations.
const { DefaultAzureCredential } = require('@azure/identity');
const { SecretClient } = require('@azure/keyvault-secrets');
const unorm = require('unorm');
const credential = new DefaultAzureCredential();
const vaultUrl = process.env.KEY_VAULT_URL;
const client = new SecretClient(vaultUrl, credential);
async function getSecretNormalized(secretName) {
const normalizedName = unorm.nfc(secretName);
return await client.getSecret(normalizedName);
}
async function setSecretNormalized(secretName, value) {
const normalizedName = unorm.nfc(secretName);
return await client.setSecret(normalizedName, value);
}
Azure Storage services require similar normalization strategies. When working with blob names and container names, always normalize to NFC before creating or accessing storage entities.
const { BlobServiceClient } = require('@azure/storage-blob');
const unorm = require('unorm');
const blobServiceClient = BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION_STRING);
async function createNormalizedContainer(containerName) {
const normalizedName = unorm.nfc(containerName);
return await blobServiceClient.createContainer(normalizedName);
}
async function getNormalizedBlob(containerName, blobName) {
const containerClient = blobServiceClient.getContainerClient(unorm.nfc(containerName));
const blobClient = containerClient.getBlobClient(unorm.nfc(blobName));
return await blobClient.download();
}
For Azure API Management, implement normalization policies that ensure consistent character handling across all APIs. Use the <rewrite-uri> and <set-variable> policies to normalize query parameters and headers before they reach backend services.
Consider implementing a global normalization middleware for your Azure applications that automatically normalizes all string inputs and outputs. This ensures consistent behavior across your entire Azure ecosystem, regardless of the specific service or programming language used.