Azure Storage Security & Guardrails
Azure Storage Security & Guardrails
Section titled “Azure Storage Security & Guardrails”This guide covers essential security configurations, guardrails, and request limiting strategies to protect your Azure Blob Storage and control access to your image assets.
🔐 Security Fundamentals
Section titled “🔐 Security Fundamentals”1.1 Access Control Levels
Section titled “1.1 Access Control Levels”Azure Storage provides multiple layers of access control:
- Account Level: Storage account access keys and SAS
 - Container Level: Container permissions and access policies
 - Blob Level: Individual blob permissions
 - Network Level: Firewall rules and virtual networks
 
1.2 Authentication Methods
Section titled “1.2 Authentication Methods”- Storage Account Keys (Shared Key)
 - Shared Access Signatures (SAS)
 - Azure Active Directory (Azure AD)
 - Anonymous Public Access (Limited scenarios)
 
🛡️ Essential Security Configurations
Section titled “🛡️ Essential Security Configurations”2.1 Disable Storage Account Keys (Recommended)
Section titled “2.1 Disable Storage Account Keys (Recommended)”# Disable shared key access for enhanced securityaz storage account update \  --name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --allow-shared-key-access false
# Enable Azure AD authentication onlyaz storage account update \  --name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --enable-azure-ad-ds false2.2 Configure Minimum TLS Version
Section titled “2.2 Configure Minimum TLS Version”# Enforce TLS 1.2 minimumaz storage account update \  --name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --min-tls-version TLS1_2 \  --https-only true2.3 Enable Blob Soft Delete
Section titled “2.3 Enable Blob Soft Delete”# Enable soft delete for containers (up to 365 days)az storage account blob-service-properties update \  --account-name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --enable-container-delete-retention true \  --container-delete-retention-days 7
# Enable soft delete for blobsaz storage account blob-service-properties update \  --account-name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --enable-delete-retention true \  --delete-retention-days 30🔒 Shared Access Signatures (SAS) Best Practices
Section titled “🔒 Shared Access Signatures (SAS) Best Practices”3.1 Generate Account-Level SAS with Restrictions
Section titled “3.1 Generate Account-Level SAS with Restrictions”# Generate restrictive account SASaz storage account generate-sas \  --account-name $STORAGE_ACCOUNT \  --account-key $STORAGE_KEY \  --services b \  --resource-types sco \  --permissions r \  --expiry "2024-12-31T23:59:59Z" \  --https-only \  --start "2024-01-01T00:00:00Z"3.2 Container-Level SAS for Specific Access
Section titled “3.2 Container-Level SAS for Specific Access”# Generate container SAS with read-only accessaz storage container generate-sas \  --account-name $STORAGE_ACCOUNT \  --account-key $STORAGE_KEY \  --name images \  --permissions r \  --expiry "2024-06-30T23:59:59Z" \  --https-only \  --ip "203.0.113.0/24"3.3 Blob-Level SAS for Individual Files
Section titled “3.3 Blob-Level SAS for Individual Files”# Generate blob-specific SASaz storage blob generate-sas \  --account-name $STORAGE_ACCOUNT \  --account-key $STORAGE_KEY \  --container-name images \  --name "private-document.pdf" \  --permissions r \  --expiry "2024-03-15T23:59:59Z" \  --https-only🔥 Network Security & Firewall Configuration
Section titled “🔥 Network Security & Firewall Configuration”4.1 Configure Storage Account Firewall
Section titled “4.1 Configure Storage Account Firewall”# Set default action to denyaz storage account update \  --name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --default-action Deny
# Allow specific IP ranges (your office, CI/CD systems)az storage account network-rule add \  --account-name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --ip-address "203.0.113.0/24"
# Allow Azure servicesaz storage account update \  --name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --bypass AzureServices4.2 Virtual Network Integration
Section titled “4.2 Virtual Network Integration”# Create virtual network and subnetaz network vnet create \  --resource-group $RESOURCE_GROUP \  --name MyVNet \  --address-prefix 10.0.0.0/16 \  --subnet-name MySubnet \  --subnet-prefix 10.0.1.0/24
# Enable service endpoint for storageaz network vnet subnet update \  --resource-group $RESOURCE_GROUP \  --vnet-name MyVNet \  --name MySubnet \  --service-endpoints Microsoft.Storage
# Add VNet rule to storage accountaz storage account network-rule add \  --account-name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --vnet MyVNet \  --subnet MySubnet4.3 Private Endpoints (Premium Security)
Section titled “4.3 Private Endpoints (Premium Security)”# Create private endpointaz network private-endpoint create \  --resource-group $RESOURCE_GROUP \  --name storage-private-endpoint \  --vnet-name MyVNet \  --subnet MySubnet \  --private-connection-resource-id "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT" \  --group-ids blob \  --connection-name storage-connection
# Create DNS zone for private endpointaz network private-dns zone create \  --resource-group $RESOURCE_GROUP \  --name "privatelink.blob.core.windows.net"
# Link DNS zone to VNetaz network private-dns link vnet create \  --resource-group $RESOURCE_GROUP \  --zone-name "privatelink.blob.core.windows.net" \  --name MyDNSLink \  --virtual-network MyVNet \  --registration-enabled false⚡ Request Limiting & Rate Limiting
Section titled “⚡ Request Limiting & Rate Limiting”5.1 Storage Account Limits (Built-in)
Section titled “5.1 Storage Account Limits (Built-in)”Azure Storage has built-in limits per account:
- Request rate: Up to 20,000 requests/second for hot storage
 - Ingress: Up to 25 Gbps (US regions)
 - Egress: Up to 50 Gbps (US regions)
 - IOPS: Up to 20,000 (for premium storage)
 
5.2 Implement Application-Level Rate Limiting
Section titled “5.2 Implement Application-Level Rate Limiting”Node.js with Express Rate Limiting:
const rateLimit = require('express-rate-limit');const { BlobServiceClient } = require('@azure/storage-blob');
// Rate limiting middlewareconst imageRequestLimiter = rateLimit({  windowMs: 15 * 60 * 1000, // 15 minutes  max: 100, // Limit each IP to 100 requests per windowMs  message: 'Too many image requests from this IP, please try again later.',  standardHeaders: true,  legacyHeaders: false,});
// Apply rate limiting to image routesapp.use('/api/images', imageRequestLimiter);
// Track and limit per-user requestsconst userRequestTracker = new Map();
app.get('/api/images/:imageName', async (req, res) => {  const clientIP = req.ip;  const userId = req.headers['x-user-id'] || clientIP;
  // Check daily limit per user  const today = new Date().toDateString();  const key = `${userId}-${today}`;
  if (!userRequestTracker.has(key)) {    userRequestTracker.set(key, 0);  }
  const currentRequests = userRequestTracker.get(key);
  if (currentRequests >= 1000) { // Daily limit of 1000 requests per user    return res.status(429).json({      error: 'Daily request limit exceeded',      limit: 1000,      resetTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()    });  }
  userRequestTracker.set(key, currentRequests + 1);
  try {    // Generate SAS token for the image    const sasToken = generateImageSAS(req.params.imageName);    res.json({ imageUrl: `${baseImageUrl}/${req.params.imageName}?${sasToken}` });  } catch (error) {    res.status(500).json({ error: 'Failed to generate image URL' });  }});5.3 Azure CDN Rate Limiting
Section titled “5.3 Azure CDN Rate Limiting”Configure rate limiting rules in Azure CDN:
# Create CDN profile with rate limiting capabilitiesaz cdn profile create \  --resource-group $RESOURCE_GROUP \  --name cdn-with-rate-limits \  --sku Premium_Verizon  # Required for advanced rules
# The rate limiting rules would be configured through Azure Portal# under CDN Profile > Rules EngineCDN Rate Limiting Rule Example:
{  "name": "RateLimitRule",  "order": 1,  "conditions": [    {      "name": "RequestRate",      "parameters": {        "requestRate": "100",        "duration": "60",        "operator": "GreaterThan"      }    }  ],  "actions": [    {      "name": "CacheExpiration",      "parameters": {        "cacheBehavior": "Override",        "cacheType": "All",        "cacheDuration": "00:05:00"      }    }  ]}📊 Monitoring & Alerting
Section titled “📊 Monitoring & Alerting”6.1 Enable Storage Analytics & Metrics
Section titled “6.1 Enable Storage Analytics & Metrics”# Enable storage analytics loggingaz storage logging update \  --account-name $STORAGE_ACCOUNT \  --account-key $STORAGE_KEY \  --services b \  --log rwd \  --retention 90
# Enable metricsaz storage metrics update \  --account-name $STORAGE_ACCOUNT \  --account-key $STORAGE_KEY \  --services b \  --api true \  --hour true \  --minute false \  --retention 906.2 Create Security Alerts
Section titled “6.2 Create Security Alerts”# Create action group for security alertsaz monitor action-group create \  --resource-group $RESOURCE_GROUP \  --name "security-alerts" \  --action email security admin@yourdomain.com \  --action webhook https://your-security-webhook.com/alert
# Alert for unusual access patternsaz monitor metrics alert create \  --name "High Blob Request Rate" \  --resource-group $RESOURCE_GROUP \  --scopes "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT" \  --condition "avg Transactions > 10000" \  --action security-alerts \  --description "Alert when blob requests exceed normal patterns"
# Alert for unauthorized access attemptsaz monitor metrics alert create \  --name "Unauthorized Access" \  --resource-group $RESOURCE_GROUP \  --scopes "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT" \  --condition "total ClientOtherError > 50" \  --action security-alerts \  --description "Alert on multiple client authentication errors"💰 Cost Control & Budget Alerts
Section titled “💰 Cost Control & Budget Alerts”7.1 Set Up Cost Alerts
Section titled “7.1 Set Up Cost Alerts”# Create budget for storage accountaz consumption budget create \  --resource-group $RESOURCE_GROUP \  --budget-name "storage-monthly-budget" \  --amount 100 \  --time-grain Monthly \  --start-date "2024-01-01" \  --notifications '[    {      "enabled": true,      "operator": "GreaterThan",      "threshold": 80,      "contactEmails": ["admin@yourdomain.com"]    },    {      "enabled": true,      "operator": "GreaterThan",      "threshold": 100,      "contactEmails": ["admin@yourdomain.com"]    }  ]'7.2 Implement Usage-Based Pricing Protection
Section titled “7.2 Implement Usage-Based Pricing Protection”// Cost tracking middlewareconst costTracker = {  // Estimate cost per operation  calculateRequestCost(operation, count = 1) {    const costs = {      'read': 0.0004 / 10000,  // $0.0004 per 10K read operations      'write': 0.05 / 10000,   // $0.05 per 10K write operations      'list': 0.05 / 10000,    // $0.05 per 10K list operations      'delete': 0.0004 / 10000 // $0.0004 per 10K delete operations    };
    return (costs[operation] || 0) * count;  },
  // Track daily costs per user  async trackUserCost(userId, operation, count = 1) {    const cost = this.calculateRequestCost(operation, count);    const today = new Date().toDateString();    const key = `cost-${userId}-${today}`;
    // Store in your preferred database/cache    const currentCost = await redis.get(key) || 0;    const newCost = parseFloat(currentCost) + cost;
    await redis.set(key, newCost, 'EX', 86400); // Expire after 24 hours
    return newCost;  }};
// Usage in API endpointsapp.get('/api/images/:imageName', async (req, res) => {  const userId = req.headers['x-user-id'] || req.ip;
  // Track cost  const dailyCost = await costTracker.trackUserCost(userId, 'read');
  // Implement cost limit (e.g., $1 per user per day)  if (dailyCost > 1.00) {    return res.status(429).json({      error: 'Daily cost limit exceeded',      currentCost: dailyCost,      limit: 1.00    });  }
  // Continue with request...});🔍 Security Monitoring & Audit
Section titled “🔍 Security Monitoring & Audit”8.1 Enable Azure Security Center
Section titled “8.1 Enable Azure Security Center”# Enable Security Center for the subscriptionaz security auto-provisioning-setting update \  --name default \  --auto-provision on
# Enable threat detection for storage accountsaz security setting update \  --name "MCAS" \  --enabled true8.2 Implement Custom Security Monitoring
Section titled “8.2 Implement Custom Security Monitoring”// Security monitoring serviceclass SecurityMonitor {  constructor(storageAccount) {    this.storageAccount = storageAccount;    this.suspiciousPatterns = new Set();  }
  // Monitor for suspicious access patterns  async analyzeRequest(req) {    const clientIP = req.ip;    const userAgent = req.headers['user-agent'];    const timestamp = new Date();
    // Check for suspicious patterns    const patterns = {      rapidRequests: await this.checkRapidRequests(clientIP),      unusualUserAgent: this.checkUnusualUserAgent(userAgent),      geographicAnomaly: await this.checkGeographicAnomaly(clientIP),      timeAnomaly: this.checkTimeAnomaly(timestamp)    };
    const suspiciousScore = Object.values(patterns)      .filter(Boolean).length;
    if (suspiciousScore >= 2) {      await this.triggerSecurityAlert(clientIP, patterns);      return false; // Block request    }
    return true; // Allow request  }
  async checkRapidRequests(clientIP) {    const key = `requests-${clientIP}`;    const count = await redis.incr(key);    await redis.expire(key, 60); // 1 minute window
    return count > 100; // More than 100 requests per minute  }
  checkUnusualUserAgent(userAgent) {    const commonBots = [      'bot', 'crawler', 'spider', 'scraper',      'wget', 'curl', 'python-requests'    ];
    return commonBots.some(bot =>      userAgent?.toLowerCase().includes(bot)    );  }
  async triggerSecurityAlert(clientIP, patterns) {    const alert = {      timestamp: new Date(),      clientIP,      patterns,      severity: 'HIGH',      action: 'BLOCKED'    };
    // Log to security monitoring system    console.log('SECURITY ALERT:', JSON.stringify(alert));
    // Send to security team    await this.notifySecurityTeam(alert);
    // Temporarily block IP    await this.temporaryBlockIP(clientIP);  }
  async temporaryBlockIP(clientIP) {    // Add to temporary blocklist for 1 hour    await redis.set(`blocked-${clientIP}`, '1', 'EX', 3600);  }}📋 Security Checklist
Section titled “📋 Security Checklist”Essential Security Measures
Section titled “Essential Security Measures”- Disable Storage Account Keys (use Azure AD when possible)
 - Enable HTTPS-only access
 - Set minimum TLS version to 1.2
 - Configure firewall rules to restrict access
 - Enable soft delete for blobs and containers
 - Implement proper CORS settings
 - Use SAS tokens for controlled access
 - Enable logging and monitoring
 - Set up security alerts
 - Regular security reviews
 
Advanced Security (High-Value Assets)
Section titled “Advanced Security (High-Value Assets)”- Implement Private Endpoints
 - Use Customer-Managed Keys (CMK)
 - Enable Azure AD authentication
 - Implement network isolation
 - Use Azure Security Center recommendations
 - Regular penetration testing
 - Implement Zero Trust architecture
 
Rate Limiting & Cost Control
Section titled “Rate Limiting & Cost Control”- Application-level rate limiting
 - Per-user request quotas
 - CDN rate limiting rules
 - Cost monitoring and alerts
 - Usage-based access controls
 - Regular cost reviews
 
🔧 Advanced Security Configurations
Section titled “🔧 Advanced Security Configurations”9.1 Customer-Managed Keys (CMK)
Section titled “9.1 Customer-Managed Keys (CMK)”# Create Key Vaultaz keyvault create \  --resource-group $RESOURCE_GROUP \  --name "mykeyvault$RANDOM" \  --location $LOCATION \  --enable-purge-protection true
# Create encryption keyaz keyvault key create \  --vault-name "mykeyvault$RANDOM" \  --name "storage-encryption-key" \  --kty RSA \  --size 2048
# Configure storage account to use CMKaz storage account update \  --name $STORAGE_ACCOUNT \  --resource-group $RESOURCE_GROUP \  --encryption-key-source Microsoft.Keyvault \  --encryption-key-vault "https://mykeyvault$RANDOM.vault.azure.net/" \  --encryption-key-name "storage-encryption-key"9.2 Immutable Blob Storage (WORM)
Section titled “9.2 Immutable Blob Storage (WORM)”# Create container with immutable policyaz storage container create \  --name immutable-images \  --account-name $STORAGE_ACCOUNT \  --account-key $STORAGE_KEY
# Set immutability policy (for compliance requirements)az storage container immutability-policy create \  --account-name $STORAGE_ACCOUNT \  --container-name immutable-images \  --period 365 \  --allow-protected-append-writes false🚨 Incident Response Plan
Section titled “🚨 Incident Response Plan”10.1 Security Incident Checklist
Section titled “10.1 Security Incident Checklist”Immediate Response:
- Identify scope of the incident
 - Temporarily block suspicious IPs
 - Rotate storage account keys
 - Review access logs
 - Notify stakeholders
 
Investigation:
- Analyze logs for attack patterns
 - Check for data exfiltration
 - Identify compromised assets
 - Document findings
 
Recovery:
- Remove malicious access
 - Restore from backups if needed
 - Update security configurations
 - Monitor for additional activity
 
10.2 Automated Incident Response
Section titled “10.2 Automated Incident Response”// Automated incident responseclass IncidentResponse {  async handleSecurityIncident(incident) {    // Immediate actions    await this.blockSuspiciousIPs(incident.sourceIPs);    await this.rotateAccessKeys();    await this.notifySecurityTeam(incident);
    // Generate incident report    const report = await this.generateIncidentReport(incident);    await this.logIncident(report);
    return report;  }
  async blockSuspiciousIPs(ips) {    for (const ip of ips) {      await this.addFirewallRule('DENY', ip);    }  }
  async rotateAccessKeys() {    // Rotate storage account keys    await this.executeAzureCLI([      'az', 'storage', 'account', 'keys', 'renew',      '--account-name', this.storageAccount,      '--key', 'key1'    ]);  }}🔗 Related Resources
Section titled “🔗 Related Resources”- Azure Blob Storage for Images
 - GitHub Static Web Apps Integration
 - Azure Storage Best Practices Documentation
 
📞 Support & Compliance
Section titled “📞 Support & Compliance”For critical security incidents or compliance questions:
- Azure Security Center recommendations
 - Microsoft Security Response Center (MSRC)
 - Azure support tickets for security issues
 - Regular security audits and compliance reviews