How are element and attribute paths defined in XML?
Short answer
The XML body is parsed as a tree; element paths are joined with . for nested levels (user.address.city), and attributes are written with the _attr node below the element: user._attr.type for <user type="admin"> (What Is XML?). This notation is used both in the Allowed XML Keys list in the Xml tab and in the Argument Name field of the argument rule; it is exact and case-sensitive. The Allowed XML Keys list must also contain the container paths: the intermediate elements (user, user.address) and, for elements that carry an attribute, both user._attr and user._attr.type. Sibling elements repeated with the same name are gathered under a single path. XML comments and processing instructions are not inspected; there is no required key list for XML.
Problem: Paths Cannot Be Written Like XPath in the XML Service
On an XML or SOAP endpoint, only the expected elements and attributes are to be allowed, or the value of a specific attribute is to be restricted. TR7 defines paths not with XPath syntax, but with dotted path notation.
Path Notation
| XML | Paths that must be in the list |
|---|---|
<user><name>john</name></user> | user, user.name |
<user type="admin"><name>john</name></user> | user, user._attr, user._attr.type, user.name |
<user><address><city>X</city></address></user> | user, user.address, user.address.city |
<user><roles><item>a</item><item>b</item></roles></user> | user, user.roles, user.roles.item (every item is inspected through this path) |
<a x="1">metin</a> | a, a._attr, a._attr.x (the text value is inspected through the a path) |
The root element is the first part of the path. Element names are compared as written in the request (as in ns:user for prefixed names). Value inspections (length, pattern, OWASP check) are applied to element texts and attribute values.
Solution: Three Methods
Methods 1 and 2 do different jobs and can be used together; Method 3 is the command line counterpart.
| Your situation | Suitable method |
|---|---|
| I will allow only specific elements and attributes in the body | Method 1: Allowed XML Keys |
| I will restrict the value of a specific element or attribute | Method 2: Xml argument rule |
| I want to do it with a script or the command line | Method 3: WEB-CLI |
Method 1: Allowed XML Keys Recommended
- How it works Every distinct path in the body (containers and
_attrnodes included) is compared with the list; every path that is not in the list is a 24-point violation in the Structure Policies category. - Where it is opened Left Monitor Panel → WAF-Protected vServices → vService → WAF Management → N page types → rule → Xml → Add Common Rule for Xml
- RequirementArgument Checks on; the list not longer than the Max. Allowed Arguments value
- vService type WAF-enabled HTTP vService
Step-by-Step Configuration
Argument Checks comes on when WAF is turned on; if it has been turned off, in the Left Monitor Panel → WAF-Protected vServices → vService → WAF Management tab, the pencil icon at the top right of the Checks card in the Inspection & Blocking section is clicked (Checks Edit screen).
Argument Checks is set to the on position; if the number of paths is greater than the Max. Allowed Arguments value (default 25), the value is increased.
Edit is clicked.
On the WAF Management tab, the N page types row of the host group is clicked (accessing the Page Types screen).
The page type of the XML endpoint is clicked; the detail panel opens on the right (accessing the panel).
The Xml tab is clicked in the tab strip of the panel.
Add Common Rule for Xml is clicked; if the rule has its own Xml common rule, the button appears as Edit Common Rule for Xml (Add Common Rule for XML screen). The add form opens filled with the Xml values of the Any Paths rule.
The Allowed XML Keys switch is set to the Restrict position and every path, containers included, is added to the list as a separate item (e.g. user, user._attr, user._attr.type, user.name).
Add (Edit when editing) is clicked; the change is applied without a separate deployment step. In a cluster setup, it is also sent to the peer device with the default auto-sync; if Manual Sync Mode is on, it is sent to the peer device manually.
Verification
While the host group is in Blocking mode, an XML is sent with an attribute that is not in the list.
Command: curl -s -o /dev/null -w "%{http_code}\n" -H "Content-Type: application/xml" -d '<user type="admin" role="root"><name>john</name></user>' https://www.example.com/soap
418Xml@user._attr.role in the Event Logs record.Method 2: Xml Argument Rule
- How it works A length, a pattern and an OWASP check setting are defined for the value of a single element or attribute path; for that path, the value inspections of the Xml common rule are not applied.
- Where it is opened Left Monitor Panel → WAF-Protected vServices → vService → WAF Management → N page types → rule → Xml → Add Argument Rule for Xml
- RequirementStructure Checks on for the value inspections
- vService type WAF-enabled HTTP vService
Step-by-Step Configuration
With steps 4–6 of Method 1, the Xml tab of the page type is opened.
Add Argument Rule for Xml above the variable table is clicked (Add Argument Rule for XML screen).
The full path is written in the Argument Name box (e.g. user._attr.type); the XML Value Length and XML Value Pattern fields are filled in (e.g. the pattern ^(admin|user)$).
Add is clicked.
Verification
The variable table in the rule's Xml tab is checked.
The user._attr.type row in bold; XML value pattern in its detailsMethod 3: WEB-CLI
- How it works The list in Method 1 and the argument rule in Method 2 are written with WEB-CLI.
- Where it is opened WEB-CLI →
waf rule-parts - Requirement WEB-CLI access; Argument Checks on; Structure Checks on for the value inspections
- vService type WAF-enabled HTTP vService
- Pattern limitation A pattern that contains
|(e.g.^(admin|user)$) splits the command in the WEB-CLI line; such patterns are entered from the interface
Step-by-Step Configuration
The ID of the default host group is defaultHost; rule IDs are listed in the id column:
waf fw-rules list poolId <vService> hostGroupId defaultHostThe row that shows xml: (all keys) in the description column is the Xml common part:
waf rule-parts list poolId <vService> hostGroupId defaultHost ruleId <rule>Since the xmlBlockAll default is on in the add command, --xmlBlockAll false is given; the fields that are not written come with the command's defaults (e.g. valueLength 0–1024):
waf rule-parts add poolId <vService> hostGroupId defaultHost ruleId <rule> loc xml --xmlBlockAll false --xmlAllowedArgs.0 user --xmlAllowedArgs.1 user._attr --xmlAllowedArgs.2 user._attr.type --xmlAllowedArgs.3 user.nameFirst, the existing xmlAllowedArgs.N lines are seen with waf rule-parts show … <part>; since --xmlAllowedArgs.N replaces the existing path at that position, the new path is written to the first empty position (0 if the list is empty):
waf rule-parts update poolId <vService> hostGroupId defaultHost ruleId <rule> id <part> --xmlAllowedArgs.0 userThe pattern is written in single quotes and without using |:
waf rule-parts add poolId <vService> hostGroupId defaultHost ruleId <rule> loc xml --arg user._attr.type --argRegex.input '^[a-z]{3,10}$'Verification
The parts of the rule are listed.
Command: waf rule-parts list poolId <vService> hostGroupId defaultHost ruleId <rule>
xml: (all keys) and xml: user._attr.typeCommon Mistakes
- Writing
user.typefor an attribute. The attribute path is written with_attr:user._attr.type. - Forgetting the
user._attrcontainer. The container_attrpath is also in the Allowed XML Keys list; if it is missing, every request that carries an attribute produces a violation. - Using XPath syntax (
/user/@type). Only the dotted path notation is valid. - Writing an index for a repeated element. There is no indexed path such as
user.roles.item.0; all repeated elements are inspected through theuser.roles.itempath. - Leaving
xmlBlockAllon withaddin WEB-CLI. In theaddcommand, thexmlBlockAlldefault is on; if--xmlBlockAll falseis not given while the common part is added, all XML bodies are blocked.
How it is done in the documentation
