Token Leakage on Azure
How Token Leakage Manifests in Azure
Token leakage in Azure environments typically occurs through misconfigured Azure services, exposed storage accounts, and improper handling of authentication tokens in client-side code. Azure's extensive ecosystem creates multiple attack surfaces where tokens can be inadvertently exposed.
The most common manifestation is through Azure Blob Storage misconfigurations. When storage accounts are set to public access or when SAS (Shared Access Signature) tokens are generated with overly permissive scopes, attackers can enumerate and access sensitive data. Consider this vulnerable pattern:
const azure = require('azure-storage');
const blobService = azure.createBlobService('accountName', 'accountKey');
// Vulnerable: SAS token generation with excessive permissions
const sasToken = blobService.generateSharedAccessSignature(
'mycontainer',
{
AccessPolicy: {
Permissions: azure.BlobUtilities.SharedAccessPermissions.READ |
azure.BlobUtilities.SharedAccessPermissions.WRITE |
azure.BlobUtilities.SharedAccessPermissions.DELETE,
Start: new Date(),
Expiry: new Date(new Date().valueOf() + 24 * 60 * 60 * 1000) // 24 hours
}
}
);
// Token leakage: SAS token returned in API response
app.get('/get-sas-token', (req, res) => {
res.json({ sasToken }); // Exposes token to client
});
Another Azure-specific scenario involves Azure Key Vault misconfigurations. When Key Vault access policies grant excessive permissions or when secrets are stored in plain text, tokens and credentials can be compromised:
const msRestAzure = require('ms-rest-azure');
const KeyVault = require('azure-keyvault');
// Vulnerable: Service principal credentials hardcoded
const clientId = 'your-client-id';
const clientSecret = 'your-client-secret'; // Token leakage risk
const domain = 'your-domain';
msRestAzure.loginWithServicePrincipalSecret(clientId, clientSecret, domain, (err, credentials) => {
if (err) throw err;
const keyVaultClient = new KeyVault.KeyVaultClient(credentials);
keyVaultClient.getSecret('https://mykeyvault.vault.azure.net/secrets/mysecret', '', (err, result) => {
console.log(result); // Secret value exposed in logs
});
});
Azure Functions present unique token leakage risks through improper HTTP trigger configurations. When functions are not properly secured, they can expose tokens through HTTP responses or logs:
module.exports = async function (context, req) {
const token = req.headers['authorization']; // Token captured but not validated
// Vulnerable: Token logged without sanitization
context.log(`Received token: ${token}`); // Token leakage in logs
// Vulnerable: Token returned in response
context.res = {
body: { token: token } // Direct token exposure
};
};
Azure App Service token leakage often occurs through application settings exposure. When configuration files containing tokens are accidentally committed to repositories or when App Service configurations are not properly secured:
# Azure CLI: Insecure token storage
az webapp config appsettings set \
--name myapp \
--resource-group mygroup \
--settings \
STORAGE_ACCOUNT_KEY="my-storage-key" \
DATABASE_PASSWORD="my-db-password" # Tokens stored in plain text
Azure-Specific Detection
Detecting token leakage in Azure environments requires a multi-layered approach combining Azure-native tools with specialized scanning capabilities. Azure Security Center provides baseline monitoring, but it lacks the depth needed for comprehensive token leakage detection.
Azure Security Center can identify some token-related misconfigurations through its security policies. You can enable specific policies to detect overly permissive SAS tokens and public storage access:
# Enable Azure Policy to detect public storage accounts
az policy assignment create \
--name "DenyPublicStorage" \
--display-name "Deny Public Storage Access" \
--policy "https://raw.githubusercontent.com/Azure/azure-policy/master/samples/Storage/storage-account-public-access/DenyPublicStorageAccess.json"
# Enable diagnostic logging to detect token leakage in logs
az monitor diagnostic-settings create \
--resource-group myResourceGroup \
--name "TokenLeakageDetection" \
--resource myStorageAccount \
--logs '[{"category": "StorageRead", "enabled": true}, {"category": "StorageWrite", "enabled": true}]' \
--workspace /subscriptions/my-subscription/resourcegroups/my-workspace/providers/microsoft.operationalinsights/workspaces/my-log-analytics
For comprehensive detection, middleBrick's Azure-specific scanning capabilities provide deep analysis of token leakage vulnerabilities. The scanner examines Azure-specific attack patterns including SAS token enumeration, Key Vault misconfigurations, and Azure Functions security flaws:
# Scan Azure API endpoints with middleBrick
middlebrick scan https://myazureapi.azurewebsites.net \
--output json \
--include azure-storage,azure-keyvault,azure-functions
# GitHub Action for continuous Azure security monitoring
name: Azure Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick Azure Scan
run: |
npm install -g middlebrick
middlebrick scan https://myazureapi.azurewebsites.net --output json > security-report.json
- name: Fail on high-risk findings
run: |
SCORE=$(jq '.overall_score' security-report.json)
if [ $SCORE -lt 80 ]; then
echo "Security score below threshold: $SCORE"
exit 1
fi
Azure Application Insights can be configured to detect token patterns in telemetry data. By creating custom log queries, you can identify potential token leakage:
// Kusto query to detect token patterns in Azure logs
let tokenPatterns = dynamic([
@"Bearer [A-Za-z0-9-_.]{10,}",
@"Basic [A-Za-z0-9+/=]{10,}",
@"sk=[A-Za-z0-9-_.]{10,}",
@"sig=[A-Za-z0-9-_.]{10,}"
]);
traces
| where message has_any (tokenPatterns)
| summarize count() by cloud_RoleName, bin(timestamp, 1h)
| where count_ > 5 // Multiple token detections in an hour
| project timestamp, cloud_RoleName, count_, message
Azure-Specific Remediation
Remediating token leakage in Azure requires implementing Azure-native security controls and following Azure's security best practices. The key is to eliminate token exposure while maintaining functionality.
For Azure Blob Storage, implement managed identities and Azure AD authentication instead of SAS tokens:
const { BlobServiceClient } = require('@azure/storage-blob');
const { DefaultAzureCredential } = require('@azure/identity');
// Secure: Using managed identity instead of SAS tokens
const blobServiceClient = new BlobServiceClient(
`https://${process.env.AZURE_STORAGE_ACCOUNT_NAME}.blob.core.windows.net`,
new DefaultAzureCredential()
);
// Secure: Scoped access without token exposure
async function uploadFile(containerName, blobName, filePath) {
const containerClient = blobServiceClient.getContainerClient(containerName);
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const uploadResponse = await blockBlobClient.uploadFile(filePath, {
onProgress: (event) => {
console.log(`Uploaded ${event.loadedBytes} bytes`);
}
});
return uploadResponse.requestId;
}
// Secure: Token never exposed to client
app.get('/upload-url', async (req, res) => {
try {
const containerClient = blobServiceClient.getContainerClient('uploads');
const blobName = `${Date.now()}-${req.query.filename}`;
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
// Generate upload URL without exposing tokens
const uploadUrl = await blockBlobClient.generateBlobUploadUrl();
res.json({ uploadUrl: uploadUrl.url });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
For Azure Key Vault, implement the principle of least privilege and use Azure AD authentication:
const { DefaultAzureCredential } = require('@azure/identity');
const { KeyVaultSecretClient } = require('@azure/keyvault-secrets');
// Secure: Managed identity with least privilege
const credential = new DefaultAzureCredential();
const vaultUrl = `https://${process.env.KEY_VAULT_NAME}.vault.azure.net`;
const client = new KeyVaultSecretClient(vaultUrl, credential);
// Secure: No token exposure in code
async function getSecret(secretName) {
try {
const secret = await client.getSecret(secretName);
return secret.value; // Value returned, not the token
} catch (error) {
console.error(`Error retrieving secret: ${error.message}`);
throw error;
}
}
// Secure: Azure RBAC for access control
// In Azure CLI or Portal:
az role assignment create \
--assignee my-managed-identity \
--role "Key Vault Secrets User" \
--scope /subscriptions/my-subscription/resourceGroups/my-group/providers/Microsoft.KeyVault/vaults/my-key-vault
For Azure Functions, implement Azure AD authentication and secure HTTP triggers:
const { AppServiceTokenStore } = require('@azure/identity');
// Secure: Azure AD authentication for function
module.exports = async function (context, req) {
const credential = new AppServiceTokenStore();
try {
// Validate token before processing
const token = await credential.getToken('https://management.azure.com/.default');
if (!token) {
context.res = {
status: 401,
body: 'Unauthorized'
};
return;
}
// Process request securely
context.res = {
body: { message: 'Request processed securely' }
};
} catch (error) {
context.log.error('Authentication error:', error.message);
context.res = {
status: 500,
body: 'Internal server error'
};
}
};
Azure App Service configurations should use Key Vault references instead of plain text settings:
# Secure: Key Vault references instead of plain text
az webapp config appsettings set \
--name myapp \
--resource-group mygroup \
--settings \
[email protected](SecretUri=https://mykeyvault.vault.azure.net/secrets/storage-key) \
[email protected](SecretUri=https://mykeyvault.vault.azure.net/secrets/db-password)
# Secure: Managed identity for App Service
az webapp identity assign \
--resource-group mygroup \
--name myapp \
--role "Key Vault Secrets User" \
--scope /subscriptions/my-subscription/resourceGroups/mygroup/providers/Microsoft.KeyVault/vaults/mykeyvault