Can I allow only specific keys in JSON; how are nested key paths written?
Short answer
Yes. Allowed JSON Keys in the common rule of the JSON tab is a positive list; keys are written as a dotted full path (user.name), are case-sensitive, and array indexes do not lengthen the path: every element of the user.roles array matches through the user.roles path. The list must also contain the container paths; for the {"user":{"name":"john","roles":["admin"]}} body, all three of the user, user.name and user.roles paths are in the list. Every distinct path that is not in the list is a 24-point violation in the Structure Policies category. The same path notation is also used in the Argument Name field of the argument rule. There is no required key list in JSON. For the inspection, Argument Checks must be on in the Checks window and the list must not exceed the Max. Allowed Arguments value.
Problem: The JSON API Is Called with Unexpected Keys
Keys that are not in the schema are sent to a JSON endpoint (mass assignment or hidden field attempts, e.g. "isAdmin": true). Only the expected key paths are to be allowed in the body, and the values of individual paths are to be restricted.
How Are Key Paths Written?
A path is formed by joining the object keys with .; array indexes are not added to the path.
| Body | Paths that must be in the list |
|---|---|
{"user":{"name":"john","roles":["admin"]}} | user, user.name, user.roles |
{"items":[{"id":1,"qty":2}]} | items, items.id, items.qty |
[{"id":1}] (root array) | id |
{"a":{"b":{"c":"x"}}} | a, a.b, a.b.c |
Paths are case-sensitive; User.Name and user.name are different paths. The same full path is also written in the Argument Name field of the argument rule; the last part alone (name) does not catch the nested key.
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 paths in the body | Method 1: Allowed JSON Keys |
| I will restrict the value of a specific path | Method 2: JSON argument rule |
| I want to do it with a script or the command line | Method 3: WEB-CLI |
Method 1: Allowed JSON Keys Recommended
- How it works Every distinct path in the body (containers included) is compared with the list; every path that is not in the list counts as a violation.
- Where it is opened Left Monitor Panel → WAF-Protected vServices → vService → WAF Management → N page types → rule → JSON → Add Common Rule for JSON
- 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 in the list 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 JSON endpoint is clicked; the detail panel opens on the right (accessing the panel). If there is no page type, it is added first (How do I add a new page type manually?).
The JSON tab is clicked in the tab strip of the panel.
Add Common Rule for JSON is clicked; if the rule has its own JSON common rule, the button appears as Edit Common Rule for JSON (Add Common Rule for JSON screen). The add form opens filled with the JSON values of the Any Paths rule.
The Allowed JSON 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.name, user.roles).
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, a JSON is sent with a key that is not in the list.
Command: curl -s -o /dev/null -w "%{http_code}\n" -H "Content-Type: application/json" -d '{"user":{"name":"john","isAdmin":true}}' https://www.example.com/api/profil
418The Attacks tab of the Event Logs record is checked.
Attack area: JSON@user.isAdmin · Attack name: Allowed JSON keysMethod 2: JSON Argument Rule
- How it works A length, a pattern and an OWASP check setting are defined for the text values of a single path; for that path, the value inspections of the JSON common rule are not applied.
- Where it is opened Left Monitor Panel → WAF-Protected vServices → vService → WAF Management → N page types → rule → JSON → Add Argument Rule for JSON
- 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 JSON tab of the page type is opened.
Add Argument Rule for JSON above the variable table is clicked (Add Argument Rule for JSON screen).
The full path is written in the Argument Name box (e.g. user.name); the JSON Value Length and JSON Value Pattern fields are filled in (e.g. the pattern ^[a-z]+$). The pattern and the length are applied only to text values; number and boolean values are not inspected.
Add is clicked.
Verification
The variable table in the rule's JSON tab is checked.
The user.name row in bold; JSON value pattern ^[a-z]+$ 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
- Pre-fill difference
addwrites the command's defaults to the fields that are not written (jsonBlockAllon,jsonDepth≤ 8,valueLength0–1024)
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 json: (all keys) in the description column is the JSON common part:
waf rule-parts list poolId <vService> hostGroupId defaultHost ruleId <rule>So that not all JSON bodies are blocked, --jsonBlockAll false is given:
waf rule-parts add poolId <vService> hostGroupId defaultHost ruleId <rule> loc json --jsonBlockAll false --jsonAllowedArgs.0 user --jsonAllowedArgs.1 user.name --jsonAllowedArgs.2 user.rolesFirst, the existing jsonAllowedArgs.N lines are seen with waf rule-parts show … <part>; since --jsonAllowedArgs.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> --jsonAllowedArgs.0 userThe pattern is written in single quotes:
waf rule-parts add poolId <vService> hostGroupId defaultHost ruleId <rule> loc json --arg user.name --argRegex.input '^[a-z]+$'Verification
The parts of the rule are listed.
Command: waf rule-parts list poolId <vService> hostGroupId defaultHost ruleId <rule>
json: (all keys) and json: user.nameCommon Mistakes
- Not writing the container path. The
usercontainer is also inspected; if it is not in the list, every request that carries auserobject produces a violation. - Writing an array index. Array indexes are not added to the path;
user.roles.0matches no value, and the elements of the array are inspected through theuser.rolespath. - Exceeding Max. Allowed Arguments. In deep schemas, the number of paths grows quickly; if the list exceeds the Max. Allowed Arguments value, it is not applied for that rule, and the value is increased (What is Max. Allowed Arguments for; what happens if a page's learned argument list exceeds this number?).
- Looking for a required key list. In JSON there is only an allowed path list; the presence of a key cannot be made mandatory.
- Leaving
jsonBlockAllon withaddin WEB-CLI. In theaddcommand, thejsonBlockAlldefault is on; if--jsonBlockAll falseis not given while the common part is added, all JSON bodies are blocked.
How it is done in the documentation
