Rule sets
Look up the fields of a WAF rule set, its eight threat families, the five sensitivity levels, and the 63 internal rules behind them.
A rule set is the object that holds what Web Application Firewall (WAF) looks for: the threat families it scores a request against, and the sensitivity level that turns a score into a block. Azion Console lists these under WAF Rules; the Azion API and the Azion CLI call the same object a waf.
A rule set does not act on its own. It runs when a rule in Rules Engine for Firewall carries the Set WAF behavior naming it, together with a mode of Logging or Blocking. The mode belongs to that rule and not to the rule set, so one rule set runs in Logging on one rule and in Blocking on another. For more information, refer to Scoring and modes.
This page lists the fields of a rule set, the eight threat families it scores, the five sensitivity levels and their thresholds, the 63 internal rules behind the families, and the API and CLI surfaces that manage it.
Fields
A rule set carries seven fields, four of which a request sets and three of which the platform sets and returns. The table below also lists the six nested fields inside engine_settings, so it runs to thirteen rows.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | integer | Read-only | — | The identifier every later call uses |
name | string, 1 to 250 characters | Yes | — | The rule set name, unique within the account. The Console renders it as the Name field |
active | boolean | No | true | Whether the rule set is active. The Console renders it as the Active switch |
product_version | string, 3 to 50 characters, nullable, matching \d+\.\d+ | No | 1.0 | The product version of the rule set |
last_editor | string, up to 250 characters | Read-only | — | The email of the account that last changed the rule set |
last_modified | date-time | Read-only | — | When the rule set last changed |
engine_settings | object | No | — | The scoring engine and what it scores. Its fields follow |
engine_settings.engine_version | string | No | 2021-Q3 | The engine build. 2021-Q3 is the only accepted value |
engine_settings.type | string | No | score | How the engine decides. score is the only accepted value |
engine_settings.attributes.rulesets | array of integers | No | [1] | The managed rule set the engine runs. [1] is the only accepted value |
engine_settings.attributes.thresholds | array of objects, 1 to 8 entries | No | All eight threat families at medium | One entry per threat family, each carrying the sensitivity to apply to it |
engine_settings.attributes.thresholds[].threat | string | Yes | — | One of the eight values listed in Threat families |
engine_settings.attributes.thresholds[].sensitivity | string | No | medium | One of the five values listed in Sensitivity levels |
Three fields accept exactly one value each. rulesets accepts [1], engine_version accepts 2021-Q3, and engine_settings.type accepts score. Any other value returns 400 with 10039 Invalid Choice, so a request that carries a second ruleset ID or a later engine version is rejected rather than downgraded.
thresholds holds between 1 and 8 entries. A request that names fewer than eight families is accepted and stores exactly what it sent, and a request that names the same family twice returns 500 rather than a validation error. The Console form always writes all eight. A create that sends no engine_settings at all produces a complete rule set: ruleset [1], engine 2021-Q3, type score, and all eight families at medium.
Threat families
WAF scores a request against eight threat families. Each family carries its own sensitivity, so a request blocked for one family passes for another. The Console renders the eight as the Threat Type Configuration table on a rule set’s Main Settings tab.
| Threat family | API value | What it detects |
|---|---|---|
| SQL Injection | sql_injection | Detects an attempt to insert a SQL query through the input data the client sends to the application |
| Remote File Inclusions (RFI) | remote_file_inclusion | Detects an attempt to include a remote file, usually through a script on the web server |
| Directory Traversal | directory_traversal | Detects insufficient sanitizing of user-supplied file names, where characters meaning “traverse to the parent directory” reach the file APIs |
| Cross-Site Scripting (XSS) | cross_site_scripting | Detects the injection of client-side scripts into pages other visitors view |
| File Upload | file_upload | Detects an attempt to upload a file |
| Evading Tricks | evading_tricks | Detects encoding tricks used to evade protection mechanisms |
| Unwanted Access | unwanted_access | Detects an attempt to reach vulnerable or administrative pages, and the use of security scanning bots and tools |
| Identified Attack | identified_attack | Detects known attacks against common vulnerabilities in applications and servers |
Every family defaults to medium. Unwanted Access and Identified Attack are scored like the other six, and a create that sends no thresholds array carries all eight.
Sensitivity levels
A request carries one score per threat family that fired, and each score is compared with the threshold of its own family. The request is blocked when a score reaches that threshold.
The threshold falls as the sensitivity rises. A higher sensitivity therefore blocks more requests on less evidence, and a lower sensitivity lets more requests through: highest blocks at a score of 4, and lowest blocks at 40.
| Level | API value | Blocks at a score of | What it means |
|---|---|---|---|
| Highest | highest | 4 | The narrowest tolerance. A single indicator of the family is enough to block |
| High | high | 8 | Blocks on slight evidence, well short of what Medium asks for |
| Medium | medium | 16 | The recommended level, and the default. Blocks when the request carries substantial evidence |
| Low | low | 24 | Blocks only on strong evidence, and lets borderline requests through |
| Lowest | lowest | 40 | The widest tolerance. Blocks only on the strongest evidence, and produces the fewest false positives |
One sensitivity applies to one threat family, and each family is set independently. In the Console the option carries the word Sensitivity: the default option reads Sensitivity Medium.
Lowest and Low protect the application less and block fewer legitimate requests. High and Highest do the opposite, and they raise the number of false positives when the traffic already observed in Logging mode has not covered the variety of requests the application receives.
Internal rules
Each internal rule carries a numeric identifier, and a rule that fires contributes to the score of the threat family it belongs to. The identifier is what an exception names when it allows a pattern the rule would otherwise score. For more information, refer to WAF Exceptions.
| Rule ID | What it detects |
|---|---|
0 | All rules. It carries no detection of its own and stands for every other rule in this table |
1 | Protocol compliance: a weird request the engine cannot parse |
2 | A request body too big to parse. The body is stored on disk and is not inspected |
10 | Protocol compliance: invalid HEX encoding, including null bytes |
11 | Protocol compliance: a missing or unknown Content-Type header on a POST. Applies to the request body match zone only |
12 | Protocol compliance: an invalid formatted URL |
13 | Protocol compliance: an invalid POST format |
14 | Protocol compliance: an invalid POST boundary |
15 | Protocol compliance: invalid JSON |
16 | Protocol compliance: a POST with no body |
17 | Possible SQL Injection attack: validation with libinjection_sql |
18 | Possible XSS attack: validation with libinjection_xss |
1000 | Possible SQL Injection attack: SQL keywords in Body, Path, Query String or Cookies |
1001 | Possible SQL Injection or XSS attack: double quote " in Body, Path, Query String or Cookies |
1002 | Possible SQL Injection attack: possible hex encoding 0x in Body, Path, Query String or Cookies |
1003 | Possible SQL Injection attack: MySQL comment /* in Body, Path, Query String or Cookies |
1004 | Possible SQL Injection attack: MySQL comment */ in Body, Path, Query String or Cookies |
1005 | Possible SQL Injection attack: MySQL keyword | in Body, Path, Query String or Cookies |
1006 | Possible SQL Injection attack: MySQL keyword && in Body, Path, Query String or Cookies |
1007 | Possible SQL Injection attack: MySQL comment -- in Body, Path, Query String or Cookies |
1008 | Possible SQL Injection or XSS attack: semicolon ; in Body, Path or Query String |
1009 | Possible SQL Injection attack: equal sign = in Body or Query String |
1010 | Possible SQL Injection or XSS attack: open parenthesis ( in Body, Path, Query String or Cookies |
1011 | Possible SQL Injection or XSS attack: close parenthesis ) in Body, Path, Query String or Cookies |
1013 | Possible SQL Injection or XSS attack: apostrophe ' in Body, Path, Query String or Cookies |
1015 | Possible SQL Injection attack: comma , in Body, Path, Query String or Cookies |
1016 | Possible SQL Injection attack: MySQL comment # in Body, Path, Query String or Cookies |
1017 | Possible SQL Injection attack: double at sign @@ in Body, Path, Query String or Cookies |
1100 | Possible RFI attack: scheme http:// in Body, Query String or Cookies |
1101 | Possible RFI attack: scheme https:// in Body, Query String or Cookies |
1102 | Possible RFI attack: scheme ftp:// in Body, Query String or Cookies |
1103 | Possible RFI attack: scheme php:// in Body, Query String or Cookies |
1104 | Possible RFI attack: scheme sftp:// in Body, Query String or Cookies |
1105 | Possible RFI attack: scheme zlib:// in Body, Query String or Cookies |
1106 | Possible RFI attack: scheme data:// in Body, Query String or Cookies |
1107 | Possible RFI attack: scheme glob:// in Body, Query String or Cookies |
1108 | Possible RFI attack: scheme phar:// in Body, Query String or Cookies |
1109 | Possible RFI attack: scheme file:// in Body, Query String or Cookies |
1110 | Possible RFI attack: scheme gopher:// in Body, Query String or Cookies |
1198 | Possible RCE attack: validation with log4j (Log4Shell) in Headers |
1199 | Possible RCE attack: validation with log4j (Log4Shell) in Body, Path, Query String, Headers or Cookies |
1200 | Possible Directory Traversal attack: double dot .. in Body, Path, Query String or Cookies |
1202 | Possible Directory Traversal attack: well-known probe /etc/passwd in Body, Path, Query String or Cookies |
1203 | Possible Directory Traversal attack: well-known Windows path c:\ in Body, Path, Query String or Cookies |
1204 | Possible Directory Traversal attack: well-known probe cmd.exe in Body, Path, Query String or Cookies |
1205 | Possible Directory Traversal attack: backslash \ in Body, Path, Query String or Cookies |
1206 | Possible Directory Traversal attack: slash / in Body, Query String or Cookies |
1207 | Possible Directory Traversal attack: well-known path probe /..;/ in Body, Query String or Cookies |
1208 | Possible Directory Traversal attack: well-known path probe /.;/ in Body, Query String or Cookies |
1209 | Possible Directory Traversal attack: well-known path probe /.%2e/ in Body, Query String or Cookies |
1210 | Possible Directory Traversal attack: well-known path probe /%2e./ in Body, Query String or Cookies |
1302 | Possible XSS attack: HTML open tag < in Body, Path, Query String or Cookies |
1303 | Possible XSS attack: HTML close tag > in Body, Path, Query String or Cookies |
1310 | Possible XSS attack: open square bracket [ in Body, Path, Query String or Cookies |
1311 | Possible XSS attack: close square bracket ] in Body, Path, Query String or Cookies |
1312 | Possible XSS attack: tilde character ~ in Body, Path, Query String or Cookies |
1314 | Possible XSS attack: back quote ` in Body, Path, Query String or Cookies |
1315 | Possible XSS attack: double encoding %[2|3] in Body, Path, Query String or Cookies |
1400 | Possible trick to evade protection: UTF7/8 encoding &# in Body, Path, Query String or Cookies |
1401 | Possible trick to evade protection: MS encoding %U in Body, Path, Query String or Cookies |
1402 | Possible trick to evade protection: encoded chars %20-%3F in Body, Query String or Cookies |
1500 | Possible File Upload attempt: .ph, .asp or .ht in a filename in a multipart POST carrying a file |
2001 | Possible CVE-2022-22965 attack: Tomcat Pipeline Context tampering |
Rule 0 means all rules, and an exception created against it applies to every other rule in the table.
The Console’s Rule ID dropdown offers 62 of these 63. Rule 1314 is absent from the list, and the API accepts it, so an exception for the back quote is created through the API or the CLI.
Some requests that fail rule 13 are blocked even when the rule set runs in Logging mode. For the request body size rule 2 measures, refer to Firewall limits.
API
Every operation is authenticated and sits under https://api.azion.com/v4/workspace/wafs. A request carries a token from Personal Tokens in the Authorization header under the Token scheme, and a request with a body also carries Content-Type: application/json.
| Operation | Method and path |
|---|---|
| Create a rule set | POST /wafs |
| List rule sets | GET /wafs |
| Retrieve a rule set | GET /wafs/{waf_id} |
| Replace a rule set | PUT /wafs/{waf_id} |
| Update part of a rule set | PATCH /wafs/{waf_id} |
| Delete a rule set | DELETE /wafs/{waf_id} |
| Clone a rule set | POST /wafs/{waf_id}/clone |
A create, a clone, and a partial update answer 202, and the envelope carries a state of pending beside the object. A read answers 200 and carries data alone, with no state key.
Create a rule set
The response carries 202, and not 201:
The state of pending says the rule set was accepted, and the id in data is the handle every later call uses. last_editor carries the email of the account that last changed the rule set, and the value above is a placeholder. The response also carries version_id, version_state, is_versioned, and version, which a create body does not set. The API returns the thresholds sorted by threat, whatever order the request sent them in.
List rule sets
The response carries 200 and the collection envelope below, with one rule set per entry under results, each in the shape the create response carries.
| Field | What it carries |
|---|---|
count | Rule sets the account holds |
total_pages | Pages the result divides into, at the current page_size |
page | The page this response carries |
page_size | Rule sets per page. Defaults to 10 |
next | The URL of the following page, or null |
previous | The URL of the preceding page, or null |
results | One rule set per entry, carrying the fields listed in Fields |
The endpoint accepts fields, id, name, ordering, page, page_size, and search as query parameters. page_size runs from 1 to 100 and defaults to 10. A value above 100 returns 400 with 10097 Invalid Page Size.
Retrieve, update, and delete a rule set
GET /wafs/{waf_id} answers 200 and returns the rule set under data.
PUT /wafs/{waf_id} replaces a rule set and takes the same body as a create. PATCH /wafs/{waf_id} takes only the fields sent and answers 202 with a state of pending. A PATCH that carries engine_settings replaces the whole thresholds array rather than merging it entry by entry, so a partial update sends every threat family the rule set is to keep.
DELETE /wafs/{waf_id} removes a rule set and answers 202 with a state of pending. While a rule’s Set WAF behavior still applies the rule set, the delete is refused with 26007.
Clone a rule set
A clone is a deep copy of an existing rule set, including its exceptions.
The response carries 202, a state of pending, and the new rule set: a new id, the name the request sent, and the threat families and sensitivities copied from the source. The API adds no suffix and derives no name of its own.
The body is required, and so is the name inside it. A clone sent with no body, or with an empty object, returns 400:
CLI
The Azion CLI manages rule sets under the waf noun. The flags below are the ones Azion CLI 4.23.0 carries, without the global flags every command takes.
| Command | What it does |
|---|---|
azion create waf | Creates a rule set |
azion list waf | Lists the rule sets the account holds |
azion describe waf | Returns one rule set |
azion update waf | Changes a rule set |
azion delete waf | Removes a rule set |
azion create waf and azion update waf share their flags, except that --product-version belongs to the create and --waf-id to the update.
| Flag | What it sets |
|---|---|
--name | The rule set name |
--active | true or false |
--rulesets | The managed ruleset IDs, comma-separated. 1 is the only value the API accepts |
--thresholds | Comma-separated threat=sensitivity pairs, one per threat family |
--engine-version | The engine build. 2021-Q3 is the only value the API accepts |
--type | The engine type. score is the only value the API accepts |
--product-version | The product version. azion create waf only |
--waf-id | The rule set to change. azion update waf only |
--file | A JSON file carrying the body, or - to read the body from standard input |
azion list waf takes --details, --filter to filter by name, --order-by, --page with a default of 1, and --page-size with a default of 50. azion describe waf and azion delete waf each take --waf-id.
A create that sets every threat family takes one --thresholds value:
A create that sets nothing but a name produces a complete rule set:
azion describe waf --waf-id 12347 --format json returns the stored rule set, with the keys sorted by the CLI:
Errors
A rejected request returns an errors array. Each entry carries a code, a title, a detail, the status, and a source pointer naming the field the rejection is about:
| Code | Title | Status | What causes it | What to do |
|---|---|---|---|---|
10004 | Not Found | 404 | The platform’s generic not-found. A POST to /wafs/{waf_id}/exceptions naming a waf_id that does not exist returns it | Check every segment of the path |
10009 | Unsupported Media Type | 415 | A write sent with a Content-Type other than application/json | Send Content-Type: application/json |
10018 | Blank Field | 400 | A name that is an empty string | Send a name of 1 to 250 characters |
10039 | Invalid Choice | 400 | A value outside an enum: threat, sensitivity, rulesets, engine_settings.type, or engine_version | Send one of the values the enum allows: the threat families are listed in Threat families, the sensitivity levels in Sensitivity levels, and the remaining three fields in Fields. The source pointer names the field |
10046 | Max Length | 400 | A name longer than 250 characters | Shorten the name |
10059 | Required Field | 400 | A required field is absent, such as a clone sent with no name | Add the field the source pointer names |
10067 | Internal Server Error | 500 | The same threat twice in thresholds | Send one entry per threat family |
10097 | Invalid Page Size | 400 | A page_size above 100 on a list request | Ask for 100 or fewer |
26006 | Name Already In Use | 400 | A name another rule set in the account already holds | Choose another name |
26007 | Cannot Delete WAF | 400 | A DELETE on a rule set that a Rules Engine rule still applies through a set_waf behavior | Delete each rule the error names, or point its Set WAF at another rule set, then delete the rule set again |
10067 returns 500 for a client mistake. A repeated threat family is answered with the text A server error occurred., which names neither the duplicate nor the field, so a reader who trusts the status code concludes the platform is down, and a retry returns the same 500. Its source pointer reads /data/attributes/thresholds, one segment short of the pointer every other threshold error returns.
26007 names each rule that still applies the rule set twice, as <firewall-name> - <rule-name>: once in the detail, It was not possible to perform this operation. To delete this WAF, you must first remove its usage in the following rules engine: ['<firewall-name> - <rule-name>']., and once as an entry of meta.ef_rules_using_waf. Its source pointer reads /data.