Blog Post

Azure Network Security Blog
11 MIN READ

Getting Started with Azure WAF REST API for Application Gateway: A Step-by-Step Guide

davidfrazee's avatar
davidfrazee
Icon for Microsoft rankMicrosoft
Jul 03, 2024

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.

 

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:

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/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroup}}/providers/Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/{{appGwWafPolicy}}?api-version=2024-01-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 consist of 3 main blocks; policySettings, customRules, and 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 Application Gateway.

 

Deploying an out of the box Azure WAF Policy requires providing a name, 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 Application Gateway in Central US and will use the latest DRS 2.1 Managed Ruleset and Bot Manager Ruleset 1.0. We’ve also set the WAF into ‘Prevention’ mode and enabled the policy under the policySettings.

 

{

  "location": "Central US",

  "properties": {

    "policySettings": {

        "state": "Enabled",

        "mode": "Prevention"

    },

    "managedRules": {

      "managedRuleSets": [

        {

          "ruleSetType": "Microsoft_DefaultRuleSet",

          "ruleSetVersion": "2.1",

          "ruleGroupOverrides": []

        },

        {

          "ruleSetType": "Microsoft_BotManagerRuleSet",

          "ruleSetVersion": "1.0",

          "ruleGroupOverrides": []

        }

      ]

    }

  }

}

 

 

After sending the PUT command to create the policy, . You’ll notice at the bottom of the screenshot above, the provisioningState shows Updating. 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:

Name

Type

Description

fileUploadEnforcement

boolean

Whether to allow WAF to enforce file upload limits.

fileUploadLimitinMb

integer

Maximum file upload size in MB for WAF.

requestBodyCheck

boolean

Whether to allow WAF to check request body.

requestBodyEnforcement

boolean

Whether to allow WAF to enforce request body limits.

requestBodyInspectLimitInKB

integer

Maximum inspection limit in KB for request body inspection for WAF.

maxRequestBodySizeinKb

integer

Maximum request body size in KB for WAF.

logScrubbing

 

To scrub sensitive log fields.

 

In this block of JSON under policySettings, we’re making changes to default values that were created for the policy and adding some new ones around request body inspection to better fit our needs. We’ve configured our policy to force inspection on all request bodies and to only allow request bodies up to 350 KB in size. Our WAF will inspect the entire request body size of 350 KB, as we defined under ‘requestBodyInspectLimitInKB’. Furthermore, our policy will enforce a file upload size limit of 1.5 GB.

 

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": {

        "state": "Enabled",

        "mode": "Prevention",

        "requestBodyCheck": true,

        "requestBodyEnforcement": true,

        "requestBodyInspectLimitInKB": 350,

        "maxRequestBodySizeinKb": 350,

        "fileUploadEnforcement": true,

        "fileUploadLimitinMb": 1500,

        "logScrubbing": {

            "state": "Enabled",

            "scrubbingRules": [

                {

                    "state": "Enabled",

                    "matchVariable": "RequestIPAddress",

                    "selectorMatchOperator": "EqualsAny"

                },

                {

                    "state": "Enabled",

                    "matchVariable": "RequestJSONArgNames",

                    "selectorMatchOperator": "Equals",

                    "selector": "password"

                },

                {

                    "state": "Enabled",

                    "matchVariable": "RequestHeaderNames",

                    "selectorMatchOperator": "Equals",

                    "selector": "client_id"

                }

            ]

        }

    },

 

 

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, 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": [

        {

            "name": "GeoBlockRule",

            "priority": 10,

            "state": "Enabled",

            "ruleType": "MatchRule",

            "action": "Block",

            "matchConditions": [

                {

                    "matchVariables": [

                        {

                            "variableName": "RemoteAddr"

                        }

                    ],

                    "operator": "GeoMatch",

                    "negationConditon": false,

                    "matchValues": [

                        "KP",

                        "RU"

                    ]

                }

            ]

        },

 

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 ‘rateLimitDuration’ allow you to configure either ‘OneMin’ or ‘FiveMins’ as the value and ‘rateLimitThreshold’ require an integer to be specified. Additionally, you’ll need to define the ‘groupByUserSession’ parameter determine how the requests will be grouped for the rule. You have the option to configure ‘ClientAddr’, ‘GeoLocation’, and ‘None’. To learn about rate limiting and the group by options, read more about it here.

 

Rate Limit Example:

        {

            "name": "RateLimitRequests",

            "priority": 20,

            "state": "Enabled",

            "ruleType": "RateLimitRule",

            "rateLimitDuration": "FiveMins",

            "rateLimitThreshold": 100,

            "action": "Block",

            "matchConditions": [

                {

                    "matchVariables": [

                        {

                            "variableName": "RequestUri"

                        }

                    ],

                    "operator": "Contains",

                    "negationConditon": false,

                    "matchValues": [

                        "search",

                        "login"

                    ]

                }

            ],

            "groupByUserSession": [

                {

                    "groupByVariables": [

                        {

                            "variableName": "ClientAddr"

                        }

                    ]

                }

            ]

        },

 

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",

            "priority": 30,

            "state": "Enabled",

            "ruleType": "MatchRule",

            "action": "Block",

            "matchConditions": [

                {

                    "matchVariables": [

                        {

                            "variableName": "RequestHeaders",

                            "selector": "User-Agent"

                        }

                    ],

                    "operator": "Contains",

                    "negationConditon": false,

                    "matchValues": [

                        "rv:125.0"

                    ]

                },

                {

                    "matchVariables": [

                        {

                            "variableName": "RequestUri"

                        }

                    ],

                    "operator": "Contains",

                    "negationConditon": false,

                    "matchValues": [

 

                    ]

                }

            ]

        }

    ],

 

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",

                    "ruleGroupOverrides": [

                    {

                        "ruleGroupName": "SQLI",

                        "rules": [

                            {

                                "ruleId": "942110",

                                "state": "Disabled",

                                "action": "AnomalyScoring"

                            },

                            {

                                "ruleId": "942260",

                                "state": "Disabled",

                                "action": "AnomalyScoring"

                            }

                        ]

                    },

                    {

                        "ruleGroupName": "PROTOCOL-ENFORCEMENT",

                        "rules": [

                            {

                                "ruleId": "920160",

                                "state": "Enabled",

                                "action": "Log"

                            }

                        ]

                      }

                    ]

                },

 

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, as we see with the first exclusion configured below. Exclusions can also be applied at a rule id level, so that there are more granular controls made 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.

 

"exclusions": [

                {

                    "matchVariable": "RequestCookieValues",

                    "selectorMatchOperator": "Equals",

                    "selector": "continueCode",

                    "exclusionManagedRuleSets": []

                },

                {

                    "matchVariable": "RequestArgValues",

                    "selectorMatchOperator": "StartsWith",

                    "selector": "comment",

                    "exclusionManagedRuleSets": [

                        {

                            "ruleSetType": "Microsoft_DefaultRuleSet",

                            "ruleSetVersion": "2.1",

                            "ruleGroups": [

                                {

                                    "ruleGroupName": "XSS",

                                    "rules": [

                                        {

                                            "ruleId": "941320"

                                        },

                                        {

                                            "ruleId": "941330"

                                        }

                                    ]

                                }

                            ]

                        }

                    ]

                }

            ]

 

Our example below shows how to place these blocks into the overall JSON file, all falling within the ‘managedRules’ block. Be aware that the ‘exclusions’ block is comma separated from the ‘managedRuleSets’ 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

Updated Jul 03, 2024
Version 1.0
No CommentsBe the first to comment