Loading...

Getting Started with Azure WAF REST API for Azure Front Door: A Step-by-Step Guide

Getting Started with Azure WAF REST API for Azure Front Door: A Step-by-Step Guide

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.

 

Postman-Auth.png

 

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.

 

Postman-PreRunScript.png

 

Lastly, we have our variables defined to use in our Postman Collection.

 

Postman-Variables.png

 

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:

Picture1.png

 

Example of the request URI: PUT https://management.azure.com/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroup}}/providers/Microsoft.Network/FrontDoorWebApplicationFirewallPolicies/{{fdWafPolicy}}?api-version=2024-02-01

 

Request Body:

Blog-2.png

 

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"

  }

}

 

Blog-3.png

 

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.

 

Blog-4.png

 

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:

Blog-5.png

 

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>

 

Blog-6.png

 

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": "https://learn.microsoft.com/en-us/azure/web-application-firewall/overview",

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

                }

            ]

        }

    },

 

Blog-7.png

 

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.

 

Blog-8.png

 

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.

 

Blog-9.png

 

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.

 

Blog-10.png

 

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

Published on:

Learn more
Azure Network Security Blog articles
Azure Network Security Blog articles

Azure Network Security Blog articles

Share post:

Related posts

Unified Routing – Diagnostics in Azure

You may (or may not) be aware that the diagnostics option in Unified Routing has been deprecated. It is being replaced by diagnostics in Azure...

9 hours ago

Service health and Message center: Azure Information Protection consolidation

This post is about the consolidation of Azure Information Protection communications under Microsoft Purview in Service Health and Message Cent...

10 hours ago

Switch to Azure Business Continuity Center for your at scale BCDR management needs

In response to the evolving customer requirements and environments since COVID-19, including the shift towards hybrid work models and the incr...

11 hours ago

Optimizing Azure Table Storage: Automated Data Cleanup using a PowerShell script with Azure Automate

Scenario This blog’s aim is to manage Table Storage data efficiently. Imagine you have a large Azure Table Storage that accumulates logs from ...

13 hours ago

Microsoft Fabric: Resolving Capacity Admin Permission Issues in Automate Capacity Scaling with Azure LogicApps

A while back, I published a blogpost explaining how to use Azure LogicApps to automate scaling Microsoft Fabric F capacities under the PAYG (P...

14 hours ago

The Azure Storage product group is heading to the SNIA Developer Conference 2024

The Azure Storage product group is heading to the SNIA Developer Conference (SDC) 2024 in Santa Clara, California, USA from September 16th thr...

1 day ago

ISSUE RESOLVED: Azure Lab Services - lab plan outage - September 12, 2024

Hello, Azure Lab Services is currently experiencing an outage affecting customers using Lab Plans for their service. Customers using Lab Accou...

3 days ago
Stay up to date with latest Microsoft Dynamics 365 and Power Platform news!
* Yes, I agree to the privacy policy