Skip to content

Commit 576d474

Browse files
authored
Merge pull request #955 from microsoft/Feature-27016
Network - 27016 - Rate Limiting is Enabled in Application Gateway WAF
2 parents 5f06872 + 16e101c commit 576d474

2 files changed

Lines changed: 180 additions & 0 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Azure Application Gateway Web Application Firewall (WAF) supports rate limiting through custom rules that restrict the number of requests clients can make within a specified time window. Rate limiting is a critical defense mechanism that protects applications from abuse by throttling clients that exceed defined request thresholds.
2+
3+
Without rate limiting configured, threat actors can execute brute force attacks that attempt thousands of password combinations per minute against authentication endpoints, credential stuffing attacks that test stolen credentials at scale, API abuse that extracts large volumes of data or consumes expensive backend resources, and application-layer denial of service attacks that flood endpoints with requests to exhaust server capacity.
4+
5+
Rate limiting rules use the `RateLimitRule` rule type and allow administrators to define thresholds based on request count per minute, with the ability to group requests by client IP address (using `groupBy` with `ClientAddr` variable) to track and limit individual clients. When a client exceeds the configured threshold, the WAF can block subsequent requests, log the violation, or redirect to a custom page. Unlike managed rulesets that detect attack patterns, rate limiting provides a quantitative defense that limits the impact of any volumetric attack regardless of whether the individual requests appear malicious. By configuring rate limiting on Application Gateway WAF, organizations can ensure that no single client can monopolize application resources or execute high-volume automated attacks.
6+
7+
8+
**Remediation action**
9+
10+
- [What is Azure Web Application Firewall on Azure Application Gateway?](https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/ag-overview) - Overview of WAF capabilities on Application Gateway including custom rules
11+
- [Create and use Web Application Firewall v2 custom rules on Application Gateway](https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/create-custom-waf-rules) - Step-by-step guidance on creating custom rules including rate limiting
12+
- [Web Application Firewall custom rules](https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/custom-waf-rules-overview) - Detailed documentation of custom rule types including RateLimitRule
13+
- [Rate limiting in Application Gateway WAF](https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/rate-limiting-overview) - Overview of rate limiting capabilities and configuration options
14+
15+
16+
<!--- Results --->
17+
%TestResult%
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<#
2+
.SYNOPSIS
3+
Validates that Rate Limiting rules are enabled in Application Gateway WAF custom rules.
4+
5+
.DESCRIPTION
6+
This test checks if all Azure Application Gateway WAF policies attached to Application Gateways
7+
have at least one custom rule configured with the RateLimitRule rule type and state set to Enabled.
8+
Rate limiting protects applications from brute force attacks, credential stuffing, API abuse,
9+
and volumetric denial of service attacks by throttling clients that exceed defined request thresholds.
10+
11+
.NOTES
12+
Test ID: 27016
13+
Category: Azure Network Security
14+
Required API: Azure Resource Graph - ApplicationGatewayWebApplicationFirewallPolicies
15+
#>
16+
17+
function Test-Assessment-27016 {
18+
[ZtTest(
19+
Category = 'Azure Network Security',
20+
ImplementationCost = 'Medium',
21+
MinimumLicense = ('Azure WAF'),
22+
Pillar = 'Network',
23+
RiskLevel = 'High',
24+
SfiPillar = 'Protect networks',
25+
TenantType = ('Workforce'),
26+
TestId = 27016,
27+
Title = 'Rate Limiting is Enabled in Application Gateway WAF',
28+
UserImpact = 'Low'
29+
)]
30+
[CmdletBinding()]
31+
param()
32+
33+
#region Data Collection
34+
Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose
35+
36+
$activity = 'Checking Application Gateway WAF rate limiting configuration'
37+
38+
# Check if connected to Azure
39+
Write-ZtProgress -Activity $activity -Status 'Checking Azure connection'
40+
41+
$azContext = Get-AzContext -ErrorAction SilentlyContinue
42+
if (-not $azContext) {
43+
Write-PSFMessage 'Not connected to Azure.' -Level Warning
44+
Add-ZtTestResultDetail -SkippedBecause NotConnectedAzure
45+
return
46+
}
47+
48+
Write-ZtProgress -Activity $activity -Status 'Querying Azure Resource Graph'
49+
50+
# Query all Application Gateway WAF policies attached to Application Gateways using Azure Resource Graph
51+
$argQuery = @"
52+
resources
53+
| where type =~ 'microsoft.network/applicationgatewaywebapplicationfirewallpolicies'
54+
| where coalesce(array_length(properties.applicationGateways), 0) >= 1
55+
| join kind=leftouter (
56+
resourcecontainers
57+
| where type =~ 'microsoft.resources/subscriptions'
58+
| project subscriptionName=name, subscriptionId)
59+
on subscriptionId
60+
| project
61+
PolicyName = name,
62+
PolicyId = id,
63+
SubscriptionName = subscriptionName,
64+
SubscriptionId = subscriptionId,
65+
EnabledState = tostring(properties.policySettings.state),
66+
Mode = tostring(properties.policySettings.mode),
67+
CustomRules = properties.customRules
68+
"@
69+
70+
$policies = @()
71+
try {
72+
$policies = @(Invoke-ZtAzureResourceGraphRequest -Query $argQuery)
73+
Write-PSFMessage "ARG Query returned $($policies.Count) records" -Tag Test -Level VeryVerbose
74+
}
75+
catch {
76+
Write-PSFMessage "Azure Resource Graph query failed: $($_.Exception.Message)" -Tag Test -Level Warning
77+
Add-ZtTestResultDetail -SkippedBecause NotSupported
78+
return
79+
}
80+
#endregion Data Collection
81+
82+
#region Assessment Logic
83+
$passed = $false
84+
85+
# Skip test if no policies found
86+
if ($policies.Count -eq 0) {
87+
Write-PSFMessage 'No Application Gateway WAF policies found attached to Application Gateways.' -Tag Test -Level Verbose
88+
Add-ZtTestResultDetail -SkippedBecause NotApplicable -Result 'No Application Gateway WAF policies found attached to Application Gateways.'
89+
return
90+
}
91+
92+
# Fail if any policy is disabled, not in Prevention mode, or has no enabled RateLimitRule custom rule
93+
$failingPolicies = $policies | Where-Object {
94+
$_.EnabledState -ne 'Enabled' -or
95+
$_.Mode -ne 'Prevention' -or
96+
@($_.CustomRules | Where-Object { $_.ruleType -eq 'RateLimitRule' -and $_.state -eq 'Enabled' }).Count -eq 0
97+
}
98+
99+
$passed = $failingPolicies.Count -eq 0
100+
101+
if ($passed) {
102+
$testResultMarkdown = "✅ All Application Gateway WAF policies attached to Application Gateways are enabled in Prevention mode and have at least one rate limiting rule configured and enabled.`n`n%TestResult%"
103+
}
104+
else {
105+
$testResultMarkdown = "❌ One or more Application Gateway WAF policies attached to Application Gateways are disabled, running in Detection mode, have no rate limiting rules configured, or have rate limiting rules configured but all set to Disabled state, leaving applications vulnerable to brute force and volumetric attacks.`n`n%TestResult%"
106+
}
107+
#endregion Assessment Logic
108+
109+
#region Report Generation
110+
$mdInfo = ''
111+
112+
$reportTitle = 'Application Gateway WAF policies'
113+
$portalLink = 'https://portal.azure.com/#browse/Microsoft.Network%2FapplicationGatewayWebApplicationFirewallPolicies'
114+
115+
$tableRows = ''
116+
foreach ($policy in $policies | Sort-Object SubscriptionName, PolicyName) {
117+
$policyLink = "https://portal.azure.com/#resource$($policy.PolicyId)"
118+
$subLink = "https://portal.azure.com/#resource/subscriptions/$($policy.SubscriptionId)"
119+
$policyMd = "[$(Get-SafeMarkdown $policy.PolicyName)]($policyLink)"
120+
$subMd = "[$(Get-SafeMarkdown $policy.SubscriptionName)]($subLink)"
121+
122+
$allRateLimitRules = @($policy.CustomRules | Where-Object { $_.ruleType -eq 'RateLimitRule' })
123+
$enabledRateLimitRules = @($allRateLimitRules | Where-Object { $_.state -eq 'Enabled' })
124+
$rateLimitRuleCountDisplay = if ($allRateLimitRules.Count -gt 0) { "$($allRateLimitRules.Count)" } else { '❌ 0' }
125+
$ruleStateDisplay = if ($allRateLimitRules.Count -eq 0) {
126+
'N/A'
127+
}
128+
elseif ($enabledRateLimitRules.Count -ge 1) {
129+
'✅ Enabled'
130+
}
131+
else {
132+
'❌ Disabled'
133+
}
134+
$enabledStateDisplay = if ($policy.EnabledState -eq 'Enabled') { '✅ Enabled' } else { '❌ Disabled' }
135+
$modeDisplay = if ($policy.Mode -eq 'Prevention') { '✅ Prevention' } else { '❌ Detection' }
136+
$statusDisplay = if ($policy.EnabledState -eq 'Enabled' -and $policy.Mode -eq 'Prevention' -and $enabledRateLimitRules.Count -ge 1) { '' } else { '' }
137+
138+
$tableRows += "| $policyMd | $subMd | $enabledStateDisplay | $modeDisplay | $rateLimitRuleCountDisplay | $ruleStateDisplay | $statusDisplay |`n"
139+
}
140+
141+
$formatTemplate = @'
142+
143+
## [{0}]({1})
144+
145+
| Policy name | Subscription name | Policy state | Mode | Rate limit rules count | Rule state | Status |
146+
| :---------- | :---------------- | :----------- | :--- | :--------------------- | :--------- | :----- |
147+
{2}
148+
149+
'@
150+
151+
$mdInfo = $formatTemplate -f $reportTitle, $portalLink, $tableRows
152+
$testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo
153+
#endregion Report Generation
154+
155+
$params = @{
156+
TestId = '27016'
157+
Title = 'Rate Limiting is Enabled in Application Gateway WAF'
158+
Status = $passed
159+
Result = $testResultMarkdown
160+
}
161+
162+
Add-ZtTestResultDetail @params
163+
}

0 commit comments

Comments
 (0)