D
davidfrazee
REST API plays a pivotal role in the management of resources on Azure, offering a standardized and methodical approach for handling operations such as create, read, update, and delete (CRUD). The use of HTTP methods, such as GET, POST, PUT, and DELETE, in REST API aligns with CRUD operations, making it intuitive for administrators to manipulate resources on Azure. Additionally, REST API supports a range of data formats, including JSON and XML, providing versatility in how data is consumed and transmitted. This is particularly valuable for automating workflows and enabling continuous deployment and integration practices. Focusing on Azure WAF, we'll examine its REST API integration for configuring rules, monitoring policies, and real-time threat response, vital for maintaining security in fast-paced cloud deployments. This seamless integration not only enhances security but also ensures that the management of security protocols keeps pace with the rapid deployment cycles inherent in modern cloud environments.
In the following examples, we’ll be using Postman to send our REST API requests to Azure Resource Manager to create, update, and delete the Azure WAF policy. There are other methods and tools to send REST APIs outside of Postman, such as PowerShell, Az CLI, Swagger, and more. The basics will be the same regardless of the tool or method used, just our interface will be different. To follow along, check out the prerequisites below to get started.
Prerequisites:
If you’re following along and have followed the prerequisites, you should now have your Postman Collection configured to something similar as below. Our first screenshot shows the Authorization tab in the Postman Collection. We’re going to use the Auth Type of Bearer Token and use the variable from our variables tab.
Next, we have our pre-request script that you can grab from the linked blog above. This script is needed to send the requests continuously to Azure Resource Manager.
Lastly, we have our variables defined to use in our Postman Collection.
With our Postman profiles configured, we can begin managing Azure resources outside of the Azure Portal in a quick and seamless manner. When sending a REST API request, there are mandatory fields that need to be present, depending on the type of request sent. To create a new WAF policy or to update an existing one, we’ll need to use a PUT command, which requires 4 URI parameters and a Request Body.
URI Parameters:
Example of the request URI: PUT https://management.azure.com/subscr...licies/{{fdWafPolicy}}?api-version=2024-02-01
Request Body:
When sending a GET request, we no longer require the request body, only the URI parameters to pull the information for the resource we want.
Let’s start with creating an out of the box WAF policy with only the minimum requirements. We’ll then add on as we go, setting our Policy configurations like request body inspection, creating Custom rules, and managing actions and exclusions on our Managed rulesets. The API for WAF Policies consists of 3 main blocks; properties.policySettings, properties.customRules, and properties.managedRules. Starting off, we’ll leave out the customRule block and visit that later in the document. For these examples, we’ll use Azure WAF on Azure Front Door.
Deploying an out of the box Azure WAF Policy requires providing a name, the SKU, a location, and to define which managed rulesets you want to use. With REST API, the name will be sent along with the URI parameters and not part of the request body. In Postman, ensure that the method is set to PUT, and under the ‘Body’ tab, select raw and then JSON from the drop-down selection. The following JSON will create a new Azure WAF Policy for Azure Front Door and will use the latest DRS 2.1 Managed Ruleset and Bot Manager Ruleset 1.1. We’ll set the SKU to Premium_AzureFrontDoor, since not specifying a SKU will default to Classic_AzureFrontDoor. Since the WAF policy will be associated with an Azure Front Door profile, the location of the policy will be set to Global. We’ve also set the WAF into ‘Prevention’ mode and enabled the policy under the property.policySettings.
{
"location": "Global",
"properties": {
"policySettings": {
"enabledState": "Enabled",
"mode": "Prevention"
},
"managedRules": {
"managedRuleSets": [
{
"ruleSetType": "Microsoft_DefaultRuleSet",
"ruleSetVersion": "2.1",
"ruleSetAction": "Block"
},
{
"ruleSetType": "Microsoft_BotManagerRuleSet",
"ruleSetVersion": "1.1",
"ruleSetAction": null
}
]
}
},
"sku": {
"name": "Premium_AzureFrontDoor"
}
}
After sending the PUT command to create the policy, we’ll use a GET to pull the resource and check out what’s been created. It’s important to check the provisioning state and ensure that it is in a Succeeded state before sending another PUT. Sending another PUT while the policy is updating will place the WAF policy into a failed state. As a best practice, always check the provisioning state of all your resources before committing any changes.
Using the example below, we can see the name that was passed through the URI parameters, our location, the state and mode of the policy, and which managed rulesets we’ve enabled. You’ll also notice that there are attributes of the policy that we never defined but are now present. This is because there are default values that are applied to Azure WAF policies when created if not explicitly defined, one of them being the enforcement of request body inspection.
With this simple example, we can already see how REST API is a flexible and quick way to create and manage resources in Azure. This method allows us to see exactly how a resource is configured in JSON, without having to navigate multiple blades in the Azure Portal.
With our out of the box policy set, let’s begin configuring additional policy settings to customize our Azure WAF policy to our needs.
What we’ll cover in this section will be:
In this block of JSON under properties.policySettings, we’re making changes to default values that were created for the policy around redirects, custom blocks, and JavaScript challenges to better fit our needs. If you ran a GET against the Policy, you’ll see that most of these values returned with null. In our example, we’ll set our redirect to land on the Azure WAF Overview Learn documentation, our JavaScript challenge expiration to 5 minutes, the custom block code to 403, and lastly use HTML in the custom block response body to show the Azure Front Door tracking reference ID for easier troubleshooting.
You’ll notice in the above table that the customBlockResponseBody must be specified in base64 encoding. We’ll have to take our HTML response below and encode this with base64. You’ll be able to do this conversion with any search engine tool and paste the results in the JSON body.
<html><head><title>Azure WAF Block</title></head><body bgcolor="#FFB299"><p><h1><strong>WAF Custom Response Page</strong></h1></p> <p> Please contact us with {{azure-ref}} <br></P></body></html>
To safeguard sensitive data and personally identifiable information (PII), we’ve enabled log scrubbing rules to scrub requestor IPs, password values that are sent in JSON body, and client IDs that come in as a request header.
Our block of JSON for these changes should look like below.
"policySettings": {
"enabledState": "Enabled",
"mode": "Prevention",
"redirectUrl": "Introduction to Azure Web Application Firewall",
"customBlockResponseStatusCode": 403,
"customBlockResponseBody": "PGh0bWw+PGhlYWQ+PHRpdGxlPkF6dXJlIFdBRiBCbG9jazwvdGl0bGU+PC9oZWFkPjxib2R5IGJnY29sb3I9IiNGRkIyOTkiPjxwPjxoMT48c3Ryb25nPldBRiBDdXN0b20gUmVzcG9uc2UgUGFnZTwvc3Ryb25nPjwvaDE+PC9wPiA8cD4gUGxlYXNlIGNvbnRhY3QgdXMgd2l0aCB7e2F6dXJlLXJlZn19IDxicj48L1A+PC9ib2R5PjwvaHRtbD4=",
"requestBodyCheck": "Enabled",
"javascriptChallengeExpirationInMinutes": 5,
"logScrubbing": {
"state": "Enabled",
"scrubbingRules": [
{
"matchVariable": "RequestIPAddress",
"selectorMatchOperator": "EqualsAny",
"selector": null,
"state": "Enabled"
},
{
"matchVariable": "RequestBodyJsonArgNames",
"selectorMatchOperator": "Equals",
"selector": "password",
"state": "Enabled"
},
{
"matchVariable": "RequestHeaderNames",
"selectorMatchOperator": "Equals",
"selector": "client_id",
"state": "Enabled"
}
]
}
},
Once the PUT is sent through and the WAF is in a ‘Succeeded’ state, you can run a GET like we did in the last section to check how the policy is configured.
Custom rules allow you to create your own rules that are evaluated for each request that passes through the WAF. These rules hold a higher priority than the rest of the rules in the managed rule sets. The custom rules contain a rule name, rule priority, and an array of matching conditions. If these conditions are met, an action is taken (to allow, block, javascript challenge or log). Custom rules have two different types of rules, MatchRule and RateLimitRule. Most custom rules will fall under the MatchRule type, including GeoMatch, while RateLimitRule will apply for any rule that has rate limiting configured for a specified condition.
Our first custom rule example shows how to create a Geomatch custom rule to block global regions that we don’t want to access the backend application. To learn more about Geomatch custom rules and suggested patterns, you can review this blog here. Here we are blocking North Korea (KP) and Russia (RU) and assigning a priority of 10 to the rule. The operator for this kind of custom rule will need to be ‘GeoMatch’ rather than a more common one like ‘contains’.
Geo Block Example:
"customRules": {
"rules": [
{
"name": "GeoBlockRule",
"enabledState": "Enabled",
"priority": 10,
"ruleType": "MatchRule",
"matchConditions": [
{
"matchVariable": "SocketAddr",
"selector": null,
"operator": "GeoMatch",
"negateCondition": false,
"matchValue": [
"KP",
"RU"
],
"transforms": []
}
],
"action": "Block",
"groupBy": []
},
Next, we’ll work on creating a rate limit custom rule that limits the amount of traffic coming to the request URIs of ‘search’ and ‘login’. You’ll notice there are extra parameters that need to be defined within the rule that aren’t relevant to MatchRule types. The parameters ‘rateLimitDurationInMinutes’ allow you to configure either ‘1’ or ‘5’ as the integer and ‘rateLimitThreshold’ also requires an integer to be specified. Additionally, you’ll need to define the ‘groupBy’ parameter to determine how the requests will be grouped for the rule. To learn about rate limiting, read more about it here.
Rate Limit Example:
{
"name": "RateLimitRequests",
"enabledState": "Enabled",
"priority": 20,
"ruleType": "RateLimitRule",
"rateLimitDurationInMinutes": 5,
"rateLimitThreshold": 100,
"matchConditions": [
{
"matchVariable": "RequestUri",
"selector": null,
"operator": "Contains",
"negateCondition": false,
"matchValue": [
"/search",
"/login"
],
"transforms": []
}
],
"action": "Block",
"groupBy": [
{
"variableName": "SocketAddr"
}
]
},
Finally, let’s investigate how to create a custom rule that requires multiple conditions to be met for the action to take effect. When constructing a rule that requires multiple conditions, all our parameters will be the same, with only the key difference in the ‘matchConditions’ array. The array allows us to specify multiple match variables and match values within their own block, since an array is a collection of items of the same data type. You’ll find the first block of match conditions in orange and the second block of match conditions in green. Both blocks must be fulfilled for the custom rule to take effect and for the action to execute.
Multi-Condition Example:
{
"name": "UserAgentAndUriBlock",
"enabledState": "Enabled",
"priority": 30,
"ruleType": "MatchRule",
"matchConditions": [
{
"matchVariable": "RequestHeader",
"selector": "User-Agent",
"operator": "Contains",
"negateCondition": false,
"matchValue": [
"rv:125.0"
],
"transforms": []
},
{
"matchVariable": "RequestUri",
"selector": null,
"operator": "Contains",
"negateCondition": false,
"matchValue": [
"login"
],
"transforms": []
}
],
"action": "Block",
"groupBy": []
}
]
},
This block of custom rules is fairly large, so we aren’t able to capture how this will look in our example below, but we can see how the block will fit within the JSON to apply to the policy.
With every step, once the PUT has been executed, feel free to run a GET to ensure that the new configurations have gone through and that the provisioning state is ‘Succeeded’.
We’ve already configured what managed rulesets we’ll use with the policy, so now we’ll determine rules that may need to have their actions changed or if rules need to be completely disabled to meet our needs. Below, we’ve carved out some rules under the ‘SQLI’ rule group to completely disable. We’ve also changed the ‘action’ of a rule within the ‘PROTOCOL-ENFORCEMENT’ rule group to Log, rather than disabling completely. These actions can be made against rules to assist in fine tuning the WAF, reducing the amount of false positives seen within the WAF logs.
"managedRuleSets": [
{
"ruleSetType": "Microsoft_DefaultRuleSet",
"ruleSetVersion": "2.1",
"ruleSetAction": "Block",
"ruleGroupOverrides": [
{
"ruleGroupName": "PROTOCOL-ENFORCEMENT",
"rules": [
{
"ruleId": "920160",
"enabledState": "Enabled",
"action": "Log",
"exclusions": []
}
],
"exclusions": []
},
{
"ruleGroupName": "SQLI",
"rules": [
{
"ruleId": "942160",
"enabledState": "Disabled",
"action": "AnomalyScoring",
"exclusions": []
},
{
"ruleId": "942100",
"enabledState": "Disabled",
"action": "AnomalyScoring",
"exclusions": []
}
],
"exclusions": []
}
],
"exclusions": []
},
Exclusions can be key to tuning a WAF policy to isolate specific keys or key values that could be generating many false positives for the backend application. These exclusions can be applied to the entire WAF policy, to a rule group, or directly on a rule ID. Applying exclusions at different levels allows for more granular control to meet the application needs. Below we have examples of exclusions created for RequestCookieValues globally, and for RequestArgValues that will be applied strictly to two XSS rule ids.
{
"ruleGroupName": "XSS",
"rules": [
{
"ruleId": "941320",
"enabledState": "Enabled",
"action": "AnomalyScoring",
"exclusions": [
{
"matchVariable": "RequestBodyPostArgNames",
"selectorMatchOperator": "StartsWith",
"selector": "comment"
}
]
},
{
"ruleId": "941330",
"enabledState": "Enabled",
"action": "AnomalyScoring",
"exclusions": [
{
"matchVariable": "RequestBodyPostArgNames",
"selectorMatchOperator": "StartsWith",
"selector": "comment"
}
]
}
],
"exclusions": []
}
],
"exclusions": [
{
"matchVariable": "RequestCookieNames",
"selectorMatchOperator": "Equals",
"selector": "continueCode"
}
]
Our example below shows how to place these blocks into the overall JSON file, all falling within the ‘managedRules’ block. Be aware that the global ‘exclusions’ block is comma separated from the ‘ruleGroupOverrides’ block.
With our final configurations made against the WAF policy, we can send the last PUT to Azure Resource Manager to use the policy to secure our backend web applications. Once complete, run a GET command to see how the WAF policy is configured and to save this configuration for later use. If you’ve followed along with the examples, your JSON file should be around 200 lines.
Deleting an Azure WAF policy with REST API is simple, as you only need to send the request with DELETE selected as the HTTP method. Once the request is sent, the policy will be removed from the environment.
In this guide, we’ve learned how to use the Azure REST API to create and configure an Azure Web Application Firewall policy. We have seen how to use Postman to send HTTP requests to the Azure Resource Manager endpoint and how to authenticate with Azure Entra ID. We have also explored how to customize the WAF policy by enabling or disabling rule sets, overriding rules, and adding exclusions. By using the Azure REST API, we can automate and streamline the process of securing our web applications with the WAF policy. To learn more about Azure REST API and Azure WAF, check out the referenced documentation below.
Continue reading...
Getting Started
In the following examples, we’ll be using Postman to send our REST API requests to Azure Resource Manager to create, update, and delete the Azure WAF policy. There are other methods and tools to send REST APIs outside of Postman, such as PowerShell, Az CLI, Swagger, and more. The basics will be the same regardless of the tool or method used, just our interface will be different. To follow along, check out the prerequisites below to get started.
Prerequisites:
- Link to download Postman: Postman API Platform | Sign Up for Free
- Link to blog that covers how to prepare your identities and Postman tool to send REST API commands: Azure REST APIs with Postman (2021) | Jon Gallant
If you’re following along and have followed the prerequisites, you should now have your Postman Collection configured to something similar as below. Our first screenshot shows the Authorization tab in the Postman Collection. We’re going to use the Auth Type of Bearer Token and use the variable from our variables tab.
Next, we have our pre-request script that you can grab from the linked blog above. This script is needed to send the requests continuously to Azure Resource Manager.
Lastly, we have our variables defined to use in our Postman Collection.
With our Postman profiles configured, we can begin managing Azure resources outside of the Azure Portal in a quick and seamless manner. When sending a REST API request, there are mandatory fields that need to be present, depending on the type of request sent. To create a new WAF policy or to update an existing one, we’ll need to use a PUT command, which requires 4 URI parameters and a Request Body.
URI Parameters:
Example of the request URI: PUT https://management.azure.com/subscr...licies/{{fdWafPolicy}}?api-version=2024-02-01
Request Body:
When sending a GET request, we no longer require the request body, only the URI parameters to pull the information for the resource we want.
Creating a policy
Let’s start with creating an out of the box WAF policy with only the minimum requirements. We’ll then add on as we go, setting our Policy configurations like request body inspection, creating Custom rules, and managing actions and exclusions on our Managed rulesets. The API for WAF Policies consists of 3 main blocks; properties.policySettings, properties.customRules, and properties.managedRules. Starting off, we’ll leave out the customRule block and visit that later in the document. For these examples, we’ll use Azure WAF on Azure Front Door.
Deploying an out of the box Azure WAF Policy requires providing a name, the SKU, a location, and to define which managed rulesets you want to use. With REST API, the name will be sent along with the URI parameters and not part of the request body. In Postman, ensure that the method is set to PUT, and under the ‘Body’ tab, select raw and then JSON from the drop-down selection. The following JSON will create a new Azure WAF Policy for Azure Front Door and will use the latest DRS 2.1 Managed Ruleset and Bot Manager Ruleset 1.1. We’ll set the SKU to Premium_AzureFrontDoor, since not specifying a SKU will default to Classic_AzureFrontDoor. Since the WAF policy will be associated with an Azure Front Door profile, the location of the policy will be set to Global. We’ve also set the WAF into ‘Prevention’ mode and enabled the policy under the property.policySettings.
{
"location": "Global",
"properties": {
"policySettings": {
"enabledState": "Enabled",
"mode": "Prevention"
},
"managedRules": {
"managedRuleSets": [
{
"ruleSetType": "Microsoft_DefaultRuleSet",
"ruleSetVersion": "2.1",
"ruleSetAction": "Block"
},
{
"ruleSetType": "Microsoft_BotManagerRuleSet",
"ruleSetVersion": "1.1",
"ruleSetAction": null
}
]
}
},
"sku": {
"name": "Premium_AzureFrontDoor"
}
}
After sending the PUT command to create the policy, we’ll use a GET to pull the resource and check out what’s been created. It’s important to check the provisioning state and ensure that it is in a Succeeded state before sending another PUT. Sending another PUT while the policy is updating will place the WAF policy into a failed state. As a best practice, always check the provisioning state of all your resources before committing any changes.
Using the example below, we can see the name that was passed through the URI parameters, our location, the state and mode of the policy, and which managed rulesets we’ve enabled. You’ll also notice that there are attributes of the policy that we never defined but are now present. This is because there are default values that are applied to Azure WAF policies when created if not explicitly defined, one of them being the enforcement of request body inspection.
With this simple example, we can already see how REST API is a flexible and quick way to create and manage resources in Azure. This method allows us to see exactly how a resource is configured in JSON, without having to navigate multiple blades in the Azure Portal.
Policy Settings
With our out of the box policy set, let’s begin configuring additional policy settings to customize our Azure WAF policy to our needs.
What we’ll cover in this section will be:
In this block of JSON under properties.policySettings, we’re making changes to default values that were created for the policy around redirects, custom blocks, and JavaScript challenges to better fit our needs. If you ran a GET against the Policy, you’ll see that most of these values returned with null. In our example, we’ll set our redirect to land on the Azure WAF Overview Learn documentation, our JavaScript challenge expiration to 5 minutes, the custom block code to 403, and lastly use HTML in the custom block response body to show the Azure Front Door tracking reference ID for easier troubleshooting.
You’ll notice in the above table that the customBlockResponseBody must be specified in base64 encoding. We’ll have to take our HTML response below and encode this with base64. You’ll be able to do this conversion with any search engine tool and paste the results in the JSON body.
<html><head><title>Azure WAF Block</title></head><body bgcolor="#FFB299"><p><h1><strong>WAF Custom Response Page</strong></h1></p> <p> Please contact us with {{azure-ref}} <br></P></body></html>
To safeguard sensitive data and personally identifiable information (PII), we’ve enabled log scrubbing rules to scrub requestor IPs, password values that are sent in JSON body, and client IDs that come in as a request header.
Our block of JSON for these changes should look like below.
"policySettings": {
"enabledState": "Enabled",
"mode": "Prevention",
"redirectUrl": "Introduction to Azure Web Application Firewall",
"customBlockResponseStatusCode": 403,
"customBlockResponseBody": "PGh0bWw+PGhlYWQ+PHRpdGxlPkF6dXJlIFdBRiBCbG9jazwvdGl0bGU+PC9oZWFkPjxib2R5IGJnY29sb3I9IiNGRkIyOTkiPjxwPjxoMT48c3Ryb25nPldBRiBDdXN0b20gUmVzcG9uc2UgUGFnZTwvc3Ryb25nPjwvaDE+PC9wPiA8cD4gUGxlYXNlIGNvbnRhY3QgdXMgd2l0aCB7e2F6dXJlLXJlZn19IDxicj48L1A+PC9ib2R5PjwvaHRtbD4=",
"requestBodyCheck": "Enabled",
"javascriptChallengeExpirationInMinutes": 5,
"logScrubbing": {
"state": "Enabled",
"scrubbingRules": [
{
"matchVariable": "RequestIPAddress",
"selectorMatchOperator": "EqualsAny",
"selector": null,
"state": "Enabled"
},
{
"matchVariable": "RequestBodyJsonArgNames",
"selectorMatchOperator": "Equals",
"selector": "password",
"state": "Enabled"
},
{
"matchVariable": "RequestHeaderNames",
"selectorMatchOperator": "Equals",
"selector": "client_id",
"state": "Enabled"
}
]
}
},
Once the PUT is sent through and the WAF is in a ‘Succeeded’ state, you can run a GET like we did in the last section to check how the policy is configured.
Custom Rules
Custom rules allow you to create your own rules that are evaluated for each request that passes through the WAF. These rules hold a higher priority than the rest of the rules in the managed rule sets. The custom rules contain a rule name, rule priority, and an array of matching conditions. If these conditions are met, an action is taken (to allow, block, javascript challenge or log). Custom rules have two different types of rules, MatchRule and RateLimitRule. Most custom rules will fall under the MatchRule type, including GeoMatch, while RateLimitRule will apply for any rule that has rate limiting configured for a specified condition.
Our first custom rule example shows how to create a Geomatch custom rule to block global regions that we don’t want to access the backend application. To learn more about Geomatch custom rules and suggested patterns, you can review this blog here. Here we are blocking North Korea (KP) and Russia (RU) and assigning a priority of 10 to the rule. The operator for this kind of custom rule will need to be ‘GeoMatch’ rather than a more common one like ‘contains’.
Geo Block Example:
"customRules": {
"rules": [
{
"name": "GeoBlockRule",
"enabledState": "Enabled",
"priority": 10,
"ruleType": "MatchRule",
"matchConditions": [
{
"matchVariable": "SocketAddr",
"selector": null,
"operator": "GeoMatch",
"negateCondition": false,
"matchValue": [
"KP",
"RU"
],
"transforms": []
}
],
"action": "Block",
"groupBy": []
},
Next, we’ll work on creating a rate limit custom rule that limits the amount of traffic coming to the request URIs of ‘search’ and ‘login’. You’ll notice there are extra parameters that need to be defined within the rule that aren’t relevant to MatchRule types. The parameters ‘rateLimitDurationInMinutes’ allow you to configure either ‘1’ or ‘5’ as the integer and ‘rateLimitThreshold’ also requires an integer to be specified. Additionally, you’ll need to define the ‘groupBy’ parameter to determine how the requests will be grouped for the rule. To learn about rate limiting, read more about it here.
Rate Limit Example:
{
"name": "RateLimitRequests",
"enabledState": "Enabled",
"priority": 20,
"ruleType": "RateLimitRule",
"rateLimitDurationInMinutes": 5,
"rateLimitThreshold": 100,
"matchConditions": [
{
"matchVariable": "RequestUri",
"selector": null,
"operator": "Contains",
"negateCondition": false,
"matchValue": [
"/search",
"/login"
],
"transforms": []
}
],
"action": "Block",
"groupBy": [
{
"variableName": "SocketAddr"
}
]
},
Finally, let’s investigate how to create a custom rule that requires multiple conditions to be met for the action to take effect. When constructing a rule that requires multiple conditions, all our parameters will be the same, with only the key difference in the ‘matchConditions’ array. The array allows us to specify multiple match variables and match values within their own block, since an array is a collection of items of the same data type. You’ll find the first block of match conditions in orange and the second block of match conditions in green. Both blocks must be fulfilled for the custom rule to take effect and for the action to execute.
Multi-Condition Example:
{
"name": "UserAgentAndUriBlock",
"enabledState": "Enabled",
"priority": 30,
"ruleType": "MatchRule",
"matchConditions": [
{
"matchVariable": "RequestHeader",
"selector": "User-Agent",
"operator": "Contains",
"negateCondition": false,
"matchValue": [
"rv:125.0"
],
"transforms": []
},
{
"matchVariable": "RequestUri",
"selector": null,
"operator": "Contains",
"negateCondition": false,
"matchValue": [
"login"
],
"transforms": []
}
],
"action": "Block",
"groupBy": []
}
]
},
This block of custom rules is fairly large, so we aren’t able to capture how this will look in our example below, but we can see how the block will fit within the JSON to apply to the policy.
With every step, once the PUT has been executed, feel free to run a GET to ensure that the new configurations have gone through and that the provisioning state is ‘Succeeded’.
Managed Rules Actions and Exclusions
We’ve already configured what managed rulesets we’ll use with the policy, so now we’ll determine rules that may need to have their actions changed or if rules need to be completely disabled to meet our needs. Below, we’ve carved out some rules under the ‘SQLI’ rule group to completely disable. We’ve also changed the ‘action’ of a rule within the ‘PROTOCOL-ENFORCEMENT’ rule group to Log, rather than disabling completely. These actions can be made against rules to assist in fine tuning the WAF, reducing the amount of false positives seen within the WAF logs.
"managedRuleSets": [
{
"ruleSetType": "Microsoft_DefaultRuleSet",
"ruleSetVersion": "2.1",
"ruleSetAction": "Block",
"ruleGroupOverrides": [
{
"ruleGroupName": "PROTOCOL-ENFORCEMENT",
"rules": [
{
"ruleId": "920160",
"enabledState": "Enabled",
"action": "Log",
"exclusions": []
}
],
"exclusions": []
},
{
"ruleGroupName": "SQLI",
"rules": [
{
"ruleId": "942160",
"enabledState": "Disabled",
"action": "AnomalyScoring",
"exclusions": []
},
{
"ruleId": "942100",
"enabledState": "Disabled",
"action": "AnomalyScoring",
"exclusions": []
}
],
"exclusions": []
}
],
"exclusions": []
},
Exclusions can be key to tuning a WAF policy to isolate specific keys or key values that could be generating many false positives for the backend application. These exclusions can be applied to the entire WAF policy, to a rule group, or directly on a rule ID. Applying exclusions at different levels allows for more granular control to meet the application needs. Below we have examples of exclusions created for RequestCookieValues globally, and for RequestArgValues that will be applied strictly to two XSS rule ids.
{
"ruleGroupName": "XSS",
"rules": [
{
"ruleId": "941320",
"enabledState": "Enabled",
"action": "AnomalyScoring",
"exclusions": [
{
"matchVariable": "RequestBodyPostArgNames",
"selectorMatchOperator": "StartsWith",
"selector": "comment"
}
]
},
{
"ruleId": "941330",
"enabledState": "Enabled",
"action": "AnomalyScoring",
"exclusions": [
{
"matchVariable": "RequestBodyPostArgNames",
"selectorMatchOperator": "StartsWith",
"selector": "comment"
}
]
}
],
"exclusions": []
}
],
"exclusions": [
{
"matchVariable": "RequestCookieNames",
"selectorMatchOperator": "Equals",
"selector": "continueCode"
}
]
Our example below shows how to place these blocks into the overall JSON file, all falling within the ‘managedRules’ block. Be aware that the global ‘exclusions’ block is comma separated from the ‘ruleGroupOverrides’ block.
With our final configurations made against the WAF policy, we can send the last PUT to Azure Resource Manager to use the policy to secure our backend web applications. Once complete, run a GET command to see how the WAF policy is configured and to save this configuration for later use. If you’ve followed along with the examples, your JSON file should be around 200 lines.
Deleting a Policy
Deleting an Azure WAF policy with REST API is simple, as you only need to send the request with DELETE selected as the HTTP method. Once the request is sent, the policy will be removed from the environment.
Conclusion
In this guide, we’ve learned how to use the Azure REST API to create and configure an Azure Web Application Firewall policy. We have seen how to use Postman to send HTTP requests to the Azure Resource Manager endpoint and how to authenticate with Azure Entra ID. We have also explored how to customize the WAF policy by enabling or disabling rule sets, overriding rules, and adding exclusions. By using the Azure REST API, we can automate and streamline the process of securing our web applications with the WAF policy. To learn more about Azure REST API and Azure WAF, check out the referenced documentation below.
References
- Azure REST API reference documentation | Microsoft Learn
- Policies - REST API (Azure Front Door Service) | Microsoft Learn
- Azure REST APIs with Postman (2021) | Jon Gallant
- Postman API Platform | Sign Up for Free
- What is Azure Web Application Firewall on Azure Front Door? | Microsoft Learn
- Azure Web Application Firewall DRS rule groups and rules | Microsoft Learn
- Web application firewall custom rule for Azure Front Door | Microsoft Learn
- Web application firewall exclusion lists in Azure Front Door | Microsoft Learn
- Policy settings for Web Application Firewall in Azure Front Door | Microsoft Learn
- Azure Web Application Firewall on Azure Front Door Sensitive Data Protection | Microsoft Learn
- Web application firewall rate limiting for Azure Front Door | Microsoft Learn
- Azure Web Application Firewall JavaScript challenge (preview) overview | Microsoft Learn
Continue reading...