Blog Post

Azure Network Security Blog
5 MIN READ

Azure WAF custom rules for PHP2Shell mitigation

saikishor's avatar
saikishor
Icon for Microsoft rankMicrosoft
Oct 21, 2026

Written in collaboration with Gunjan Jain​ and yuvalpery​ 

As described in our earlier blog, Addressing PHP2Shell CVE-2026-109618 and CVE-2026-109619: upgrade guidance and Azure WAF mitigations, we strongly recommend upgrading to a patched version of PHP as soon as possible. Upgrading PHP remains the recommended and complete remediation for these vulnerabilities. 

For customers who are unable to upgrade immediately, this guidance provides WAF custom rules that can help reduce exposure to selected request patterns associated with exploitation techniques for these vulnerabilities.  

WAF custom rules 

The following three custom rules can be considered based on your application configuration and traffic patterns. Because custom rules can affect legitimate application traffic, review the potential application impact and validate the rules before enabling enforcement. 

Restrict PHP session upload progress

PHP supports session upload progress for tracking file uploads. For applications that do not depend on this functionality, customers can use a custom rule to restrict multipart form requests containing PHP_SESSION_UPLOAD_PROGRESS. 

The rule checks for multipart/form-data requests whose inspected request body contains the PHP session upload progress token. 

Custom rule definitions:

Azure Front Door WAF

{  
  "name": "BlockPhpSessionUploadProgress",  
  "priority": 100,  
  "enabledState": "Enabled",  
  "ruleType": "MatchRule",  
  "action": "Block",  
  "matchConditions": [  
    {   
      "matchVariable": "RequestHeader",   
      "selector": "Content-Type",   
      "operator": "Contains",   
      "negateCondition": false,   
      "transforms": ["Lowercase"],   
      "matchValue": ["multipart/form-data"]   
    },   
    {   
      "matchVariable": "RequestBody",   
      "operator": "Contains",   
      "negateCondition": false,   
      "matchValue": ["php_session_upload_progress"],   
      "transforms": ["Lowercase"]   
    }  
  ]  
}

Application Gateway and Application Gateway for Container (AGC) WAF 

{ 
    "name": "BlockPhpSessionUploadProgress", 
    "priority": 90, 
    "ruleType": "MatchRule", 
    "action": "Block", 
    "state": "Enabled", 
    "matchConditions": [ 
      { 
        "matchVariables": [ { "variableName": "RequestHeaders", "selector": "Content-Type" } ], 
        "operator": "Contains", 
        "negationConditon": false, 
        "matchValues": [ "multipart/form-data" ], 
        "transforms": [ "Lowercase" ] 
      }, 
      { 
        "matchVariables": [ { "variableName": "PostArgs" } ], 
        "operator": "Contains", 
        "negationConditon": false, 
        "matchValues": [ "php_session_upload_progress" ], 
        "transforms": [ "Lowercase" ] 
      } 
    ] 
  } 

This rule reduces exposure through the PHP session-upload-progress path but does not address all exploitation paths associated with the vulnerabilities. 

Potential application impact: Applications that legitimately use PHP session upload progress will also match this rule. A substring match may also detect the token when it appears as harmless text or in uploaded content exposed to inspection.

Limit deeply nested form parameters

Customers can use a custom rule to identify unusually deep parameter nesting in application/x-www-form-urlencoded requests. 

The proposed rule detects request field names containing 20 or more levels of bracket nesting after URL decoding. 

Because the 20-level threshold is an application policy choice rather than a vulnerability-defined boundary, legitimate applications using deeply nested parameters may also match this rule. 

Custom rule definitions:

Azure Front Door WAF: 

{  
  "name": "PhpFormNesting20",  
  "priority": 110,  
  "enabledState": "Enabled",  
  "ruleType": "MatchRule",  
  "action": "Log",  
  "matchConditions": [  
    {  
      "matchVariable": "RequestHeader",  
      "selector": "Content-Type",  
      "operator": "BeginsWith",  
      "negateCondition": false,  
      "transforms": [  
        "Trim",  
        "Lowercase"  
      ],  
      "matchValue": [  
        "application/x-www-form-urlencoded"  
      ]  
    },  
    {  
      "matchVariable": "RequestBody",  
      "operator": "RegEx",  
      "negateCondition": false,  
      "transforms": [  
        "UrlDecode"  
      ],  
      "matchValue": [  
        "(?:^|&)[^&=\\[]+(?:\\[[^&=\\]]*\\]){20}"  
      ]  
    }  
  ]  
}  

Application Gateway and Application Gateway for Container (AGC) WAF 

{ 
    "name": "PhpFormNesting20", 
    "priority": 91, 
    "ruleType": "MatchRule", 
    "action": "Log", 
    "state": "Enabled", 
    "matchConditions": [ 
      { 
        "matchVariables": [ { "variableName": "RequestHeaders", "selector": "Content-Type" } ], 
        "operator": "BeginsWith", 
        "negationConditon": false, 
        "matchValues": [ "application/x-www-form-urlencoded" ], 
        "transforms": [ "Trim", "Lowercase" ] 
      }, 
      { 
        "matchVariables": [ { "variableName": "RequestBody" } ], 
        "operator": "Regex", 
        "negationConditon": false, 
        "matchValues": [ "(?:^|&)[^&=\\[]+(?:\\[[^&=\\]]*\\]){20}" ], 
        "transforms": [ "UrlDecode" ] 
      } 
    ] 
  } 

Recommended deployment: Start with the rule in Log mode and review matches against legitimate application traffic. Consider moving the rule to Block only after validating application compatibility. 

Limit deeply nested multipart parameters

Customers can apply a similar control to multipart/form-data requests by detecting deeply nested field names. 

The proposed rule detects multipart field names containing 20 or more levels of nesting where the relevant request content is available to WAF inspection. 

As with deeply nested URL-encoded parameters, legitimate applications may use complex multipart field structures. 

Custom rule definitions:

Azure Front Door WAF

{  
  "name": "PhpMultipartNesting20",  
  "priority": 111,  
  "enabledState": "Enabled",  
  "ruleType": "MatchRule",  
  "action": "Log",  
  "matchConditions": [  
    {  
      "matchVariable": "RequestHeader",  
      "selector": "Content-Type",  
      "operator": "BeginsWith",  
      "negateCondition": false,  
      "transforms": [  
        "Trim",  
        "Lowercase"  
      ],  
      "matchValue": [  
        "multipart/form-data"  
      ]  
    },  
    {  
      "matchVariable": "RequestBody",  
      "operator": "RegEx",  
      "negateCondition": false,  
      "matchValue": [  
        "(?i)(?:^|\\r?\\n)content-disposition:[ \\t]*form-data[^\\r\\n]*;[ \\t]*name[ \\t]*=[ \\t]*(?:\"(?:\\\\[\"\\\\]|[^\"\\r\\n\\[])+(?:\\[(?:\\\\[\"\\\\]|[^\"\\r\\n\\]])*\\]){20}|[^ \\t\\r\\n;\"\\[]+(?:\\[[^ \\t\\r\\n;\"\\]]*\\]){20})"  
      ]  
    }  
  ]  
}  

Application Gateway and Application Gateway for Container (AGC) WAF 

 { 
    "name": "PhpMultipartNesting20", 
    "priority": 92, 
    "ruleType": "MatchRule", 
    "action": "Log", 
    "state": "Enabled", 
    "matchConditions": [ 
      { 
        "matchVariables": [ { "variableName": "RequestHeaders", "selector": "Content-Type" } ], 
        "operator": "BeginsWith", 
        "negationConditon": false, 
        "matchValues": [ "multipart/form-data" ], 
        "transforms": [ "Trim", "Lowercase" ] 
      }, 
      { 
        "matchVariables": [ { "variableName": "PostArgs" } ], 
        "operator": "Regex", 
        "negationConditon": false, 
        "matchValues": [ "^[^\\[\\]]+(?:\\[[^\\]]*\\]){20}" ], 
        "transforms": [] 
      } 
    ] 
  } 

This rule targets deeply nested multipart field names. It does not restrict every multipart upload or provide complete mitigation for the underlying vulnerabilities. 

Recommended deployment: Start in Log mode, review matching traffic, and consider moving to Block only after validating the impact on your application. 

Deployment and validation considerations

We recommend applying these custom rules only to affected PHP application domains and routes, including extensionless routes handled by PHP, rather than broadly applying them to unrelated applications. 

Before enabling enforcement: 

  • Review the potential impact on legitimate application traffic. 
  • Test each custom rule independently before deploying the rules together. 
  • Start applicable rules in Log mode and review legitimate matches. 
  • Verify the WAF policy scope and custom-rule precedence. 
  • After changing a rule to Block, validate both application behavior and the corresponding WAF events. 
  • Continue to prioritize upgrading PHP even when these controls are deployed. 

Custom rules may produce false positives, and WAF request-body inspection boundaries can affect coverage. An earlier terminal Allow rule in the WAF policy can also bypass subsequent custom-rule checks. 

The custom rules described above target selected request patterns associated with exploitation techniques for CVE-2026-109618 and CVE-2026-109619. They reduce selected attack surfaces but do not provide complete protection against either vulnerability. 

You can find more information about Custom Rules on Azure WAF for Application Gateway here or for Azure Front Door here.  

Upgrading to a patched version of PHP remains the recommended and complete remediation.  

Updated Oct 26, 2026
Version 4.0

2 Comments

  • AndriiTashpulov's avatar
    AndriiTashpulov
    Copper Contributor

    Hi, could you clarify how to apply the Application Gateway WAF examples in this article?

    There appears to be a gap between the published configuration and the Azure Portal:

    1. The examples omit selector for PostArgs, but the portal requires it.
      In the current custom-rule editor, selecting “Post args” makes “Post args selector” mandatory. The article does not explain how to reproduce the selector-free condition through the portal or whether CLI/API deployment is required.
    2. The rules target parameter names, but the PostArgs inspection behavior is not explained.
      PHP_SESSION_UPLOAD_PROGRESS is typically a form-field name, and PhpMultipartNesting20 is described as detecting bracket nesting in field names. Does PostArgs without a selector inspect parameter names, values, or both? If it inspects only values, these conditions would not detect the field-name patterns described in the article.

    Entering Content-Type as the Post args selector would select a form parameter named Content-Type; it would not refer to the HTTP header, so that does not resolve the deployment gap.

    Could you provide a validated deployment procedure for all three Application Gateway rules, specify the required ruleset/engine versions, and include harmless test requests with the expected WAF log matches?

    The missing selector instructions and field-name inspection details make it difficult to establish whether the deployed rules provide the intended coverage.

    • Gunjan Jain's avatar
      Gunjan Jain
      Icon for Microsoft rankMicrosoft

      1. Azure Portal UI making the selector mandatory, looks like a portal only limitation. The CLI/API supports  PostArgs  without a selector, match against ALL post parameter values. Here is an example to create a rule without selector -

      # CLI example - no selector 
       az network application-gateway waf-policy custom-rule create \
         --policy-name <policy> --resource-group <rg> \
         --name BlockPhpSessionUploadProgress --priority 90 --action Block \
         --match-condition variable=PostArgs operator=Contains values=php_session_upload_progress transforms=Lowercase

      Or useARM template / Bicep / REST API.

      2.  PostArgs, with a selector (e.g. PostArgs:password ), only matches the value of the named parameter. PostArgs, without a selector, iterates all parsed form parameters and matches against both names and values. Hence, to match both name and value of every field in URL-encoded, multipart, and JSON bodies, including the field names of file uploads. It does not check the contents of uploaded files. So, PHP_SESSION_UPLOAD_PROGRESS  used as a field name is detected, and so is 20-level bracket nesting in multipart field names. One point noteworthy is the same text appearing in a field's value will also match.

      You would need WAF_v2 SKU with CRS 3.2, DRS 2.1 or DRS 2.2, which run on the next-generation Azure WAF engine. Turn on request body checking. Either turn on request body enforcement or set the inspection limit higher than your largest non-file form data. Otherwise,, the upload-progress field can be pushed past the inspected portion of the request.

      On a related note, our test results show adding request body enforcement is also an effective mitigation against these types of attack.