Can I apply schema-like checks (key list, depth, length) to JSON-bodied API requests?
Short answer
Yes; not as a full JSON Schema validation, but as structural limits (JSON). In the common rule on the JSON tab of a page type, the allowed key paths (Allowed JSON Keys, a dotted full path such as user.email; the container paths must also be in the list), the nesting depth (JSON Depth), the number of values (JSON Key Count), the key name length (JSON Key Length), the text value length (JSON Value Length) and pattern (JSON Value Pattern) are defined. JSON that cannot be parsed is blocked with Block Invalid JSON Contents, and every JSON body that comes to an endpoint that should not accept JSON is blocked with Block All JSON Contents. A length and pattern specific to a single field are given in the argument rule opened with Add Argument Rule for JSON. There is no type inspection (number, boolean, array), required key, array element count, repetition limit or numeric range inspection; value inspections are applied only to text values. The body is inspected as JSON if its Content-Type is of the …/json or …+json type. Every violation is a 24-point structural violation and is applied only in the Monitor and Blocking modes. In the Any Paths rule, Block Invalid JSON Contents is off by default.
Problem: The API Body Carries Unexpected Fields and Formats
An API endpoint expects a certain JSON shape; however, the client can send unexpected fields (e.g. isAdmin), excessively deep nested objects, a large number of values or very long texts. These deviations may not be caught by signature scanning: mass assignment attempts, bodies that consume resources or structures that strain the parser do not carry signatures. The JSON tab of the page type marks each of these deviations as a 24-point structural violation; the score alone exceeds the blocking threshold of every protection level (blocking threshold).
What is inspected and what is not:
| Inspection | What it measures | Any Paths default |
|---|---|---|
| Allowed JSON Keys | Every distinct path that is not in the list (containers included) is a violation; case-sensitive | No list (unrestricted) |
| JSON Depth | Every object and array is one level: {"a":{"b":1}} = 2, {"a":[{"b":1}]} = 3 | At most 64 |
| JSON Key Count | The number of values at the leaves of the tree; every array element is counted separately | At most 65536 |
| JSON Key Length | The UTF-8 byte length of every key name (the last part of the path) | 0–1024 |
| JSON Value Length | The UTF-8 byte length of every text value | 0–1048576 |
| JSON Value Pattern | The regex the text value must match (positive pattern) | No pattern |
| Block Invalid JSON Contents | A body that cannot be parsed | Off (a body that cannot be parsed goes through signature scanning as raw content) |
| Block All JSON Contents | Every JSON body that comes to this endpoint | Off |
| Type, required key, array element count, numeric range, repetition limit | - | There is no inspection |
Solution: 3 Methods
The three methods write the same JSON inspections. Prerequisite: WAF is on in the vService (How do I enable WAF protection on a vService?) and there is a page type for the endpoint, or one is added (How do I add a new page type manually?).
| Your situation | Suitable method |
|---|---|
| I want to define it from the interface | Method 1: JSON tab |
| I want to define it from the command line | Method 2: WEB-CLI |
| The API has an OpenAPI definition; I want to take the top-level field list from there | Method 3: Import (OpenAPI) |
Method 1: JSON Tab Recommended
- How it works The common rule is applied to the whole JSON body that comes to the endpoint, and the argument rule to a single key path. If a path has an argument rule, the value inspections of that path are taken only from this rule.
- Where it is opened Left Monitor Panel → WAF-Protected vServices → vService → WAF Management → N page types on the mode card of the host group → rule → JSON tab in the detail panel
- Requirement Structure Checks and Argument Checks on in Checks; modify permission in the WAF area
- vService type HTTP
Step-by-Step Configuration
In the Left Monitor Panel → WAF-Protected vServices → vService → WAF Management tab, the pencil icon of the Checks card is clicked. For Allowed JSON Keys, Argument Checks must be checked, and for the other limits and Block Invalid JSON Contents, Structure Checks must be checked; Block All JSON Contents is independent of these switches. If the list will have more paths than the Max. Allowed Arguments value (default 25), the value is increased and Edit is clicked.
In the WAF Management tab, the N page types row below the mode card of the host group is clicked; the endpoint's rule is clicked in the list and the JSON tab is selected in the detail panel on the right (accessing the screen). If there is a Rule Source row at the end of the table, the values are being inherited from Any Paths or from a glob rule.
Add Common Rule for JSON is clicked; if the rule already has its own JSON inspection, Edit Common Rule for JSON appears in the same place. The form opens filled with the values of the Any Paths rule (Add Common Rule for JSON screen).
For the example body {"order":{"id":"A1","items":[{"sku":"X","qty":2}]}}:
- The Allowed JSON Keys restriction is turned on and the
order,order.id,order.items,order.items.sku,order.items.qtypaths are added (an array index does not lengthen the path) (Can I allow only specific keys in JSON; how are nested key paths written?). - JSON Depth = Less Than or Equal To 4 (in the example, the outermost object, the
orderobject, theitemsarray and the object in the array are four levels) (Can I limit JSON / XML nesting depth?). - JSON Key Count, JSON Key Length and JSON Value Length are narrowed according to the largest values the endpoint expects.
- Block Invalid JSON Contents is checked (Can I block malformed (unparseable) JSON / XML / form bodies?).
Add in the form (Edit when editing) is clicked; there is no separate deployment step, and with the save, the inheritance ends for this rule.
If a limit specific to a single field is needed, Add Argument Rule for JSON in the same tab is clicked.
The full path is written in the Argument Name field (e.g. order.id); the last part alone (id) does not catch the nested key. JSON Value Length and JSON Value Pattern are entered; the pattern is searched anywhere in the value, and for a full match it is bounded with ^ and $ as in ^[A-Z][0-9]{1,8}$ (Can I restrict a parameter's value by length or regex pattern (Value length / Value pattern)?). The form comes pre-filled; after the save, a field left empty is not inherited from the common rule, it means no restriction.
Add in the form is clicked. The argument rule does not add the path to the Allowed JSON Keys list; the path must also be in the list separately. In a cluster, the change is sent to the peer device with the default auto-sync; if Manual Sync Mode is on, it is sent with Synchronize.
Verification
A request is sent with a key that is not in the list, and the Attacks tab of the Event Logs record is checked.
Command: curl -s -o /dev/null -w "%{http_code}\n" -H "Content-Type: application/json" -d '{"order":{"id":"A1","isAdmin":"true"}}' https://api.example.com/api/v1/orders
Allowed JSON keys · order.isAdmin (24)The common rule and the variable table are read in the rule's JSON tab.
The entered limits; no Rule Source rowMethod 2: WEB-CLI
- How it works The JSON common rule and the argument rule are written with
waf rule-parts add … loc json; if--argis not given, the part is applied to the whole body, and if it is given, to that path. - Where it is opened WEB-CLI →
waf rule-parts - Requirement WEB-CLI access and modify permission in the WAF area; the vService, host group and rule IDs; Structure Checks and Argument Checks on in Checks (Method 1, step 1)
- vService type HTTP
Step-by-Step Configuration
The host group ID is read from the output of waf host-groups list poolId <vService>, and the rule ID from the output of the following command:
waf fw-rules list poolId <vService> hostGroupId <group>In WEB-CLI, the defaults of the JSON part differ from the form in the interface: Block All JSON Contents (--jsonBlockAll) and Block Invalid JSON Contents come as true, the key length as 1–32, the depth as at most 8, the number of values as at most 1024 and the length of every text value as 0–1024; on an endpoint that carries longer text values, it is increased with --valueLength.val2. For an endpoint that accepts JSON, --jsonBlockAll false is written:
waf rule-parts add poolId <vService> hostGroupId <group> ruleId <rule> loc json --jsonBlockAll false --jsonAllowedArgs.0 order --jsonAllowedArgs.1 order.id --jsonAllowedArgs.2 order.items --jsonAllowedArgs.3 order.items.sku --jsonAllowedArgs.4 order.items.qty --jsonDepth.op "<=" --jsonDepth.val 4The command gives no output when it succeeds.
A pattern that contains $ is written in quotes:
waf rule-parts add poolId <vService> hostGroupId <group> ruleId <rule> loc json --arg order.id --valueLength.op - --valueLength.val 1 --valueLength.val2 9 --argRegex.input '^[A-Z][0-9]{1,8}$'Verification
The parts of the rule are listed.
Command: waf rule-parts list poolId <vService> hostGroupId <group> ruleId <rule>
loc json parts: the whole body and order.idMethod 3: Import (OpenAPI)
- How it works The top-level field names of the body schema are written to the Allowed JSON Keys list, and the
maxLength,patternandenumrestrictions of the fields to argument rules. Nested paths are not produced. - Where it is opened Left Monitor Panel → WAF-Protected vServices → vService → WAF Management → N page types → Import (OpenAPI)
- Requirement An OpenAPI 3.0 or Swagger 2.0 file; modify permission in the WAF area
- vService type HTTP
Step-by-Step Configuration
In the WAF Management tab, the N page types row is clicked and a backup of the rule set is taken with Export on the toolbar.
Import (OpenAPI) on the toolbar is clicked, the file is selected and the Are you sure? question is confirmed with Yes.
In the generated rule, with steps 2–5 of Method 1, the inner paths are added to the Allowed JSON Keys list and limits such as the depth and the number of values are narrowed. Details: Can I generate API protection rules automatically from my OpenAPI definition?
Verification
The Allowed JSON Keys list is read in the rule's JSON tab.
Top-level fields and manually added inner pathsCommon Mistakes
- Not adding the container path to the list. For
order.items.sku,orderandorder.itemsmust also be in the list; every container that is not in the list produces a separate violation. - Writing only the last part. If
skuis written in the argument rule,order.items.skuis not caught; the full path is written. - Putting a length or pattern on a number field. JSON value inspections are applied only to text values; number values such as
"qty": 2and boolean values such astrueare not inspected. - Not checking the Content-Type. If the body does not come with a Content-Type of the
…/jsonor…+jsontype, the JSON inspections do not run; the body is scanned as raw content (JSON requests from my API are blocked by the "Block Invalid JSON Contents" rule; why?). - Not writing
--jsonBlockAll falsein WEB-CLI. Since the default istrue, all JSON bodies that come to the endpoint become violations. - The list exceeding the Max. Allowed Arguments value. The list is disabled for that rule without a warning.
- Expecting the inspections to run in Learning mode. JSON limits are applied only in the Monitor and Blocking modes.
How it is done in the documentation
