diff --git a/_banners/waf-parameter-reference.md b/_banners/waf-parameter-reference.md new file mode 100644 index 000000000..a4c60c235 --- /dev/null +++ b/_banners/waf-parameter-reference.md @@ -0,0 +1,7 @@ +{{< banner "note" "Policy parameter reference" >}} + +You can explore the parameters for each F5 WAF for NGINX feature on the [Policy parameter reference]({{< ref "/waf/policies/parameter-reference.md" >}}) page. + +This page was previously referred to as the "Declarative Policy". + +{{< /banner >}} diff --git a/content/includes/waf/table-policy-features.md b/content/includes/waf/table-policy-features.md index be565decb..80cc1849e 100644 --- a/content/includes/waf/table-policy-features.md +++ b/content/includes/waf/table-policy-features.md @@ -11,6 +11,7 @@ | [Cookie enforcement]({{< ref "/waf/policies/cookie-enforcement.md" >}}) | By default all cookies are allowed and not enforced for integrity. The user can add specific cookies, wildcards or explicit, that will be enforced for integrity. It is also possible to set the cookie attributes: HttpOnly, Secure and SameSite for cookies found in the response. | | [Data guard]({{< ref "/waf/policies/data-guard.md" >}}) | Detects and masks Credit Card Number (CCN) and/or U.S. Social Security Number (SSN) and/or custom patterns in HTTP responses. Disabled by default. | | [Deny and Allow IP lists]({{< ref "/waf/policies/deny-allow-ip.md" >}}) | Manually define denied & allowed IP addresses as well as IP addresses to never log. | +| [Do-nothing]({{< ref "/waf/policies/do-nothing.md" >}}) | Do-nothing allows you to configure the avoidance of inspecting or parsing content of a URL. | | [Disallowed file type extensions]({{< ref "/waf/policies/disallowed-extensions.md" >}}) | Support any file type, and includes a predefined list of file types by default | | [Evasion techniques]({{< ref "/waf/policies/evasion-techniques.md" >}}) | All evasion techniques are enabled by default, and can be disabled individually. These include directory traversal, bad escaped characters and more. | | [Geolocation]({{< ref "/waf/policies/geolocation.md" >}}) | The geolocation feature allows you to configure enforcement based on the location of an object using the two-letter ISO code representing a country. | @@ -20,10 +21,13 @@ | [IP address lists]({{< ref "/waf/policies/ip-address-lists.md" >}}) | Organize lists of allowed and forbidden IP addresses across several lists with common attributes. | | [IP intelligence]({{< ref "/waf/policies/ip-intelligence.md" >}}) | Configure the IP Intelligence feature to customize enforcement based on the source IP of the request, limiting access from IP addresses with questionable reputation. | | [JWT protection]({{< ref "/waf/policies/jwt-protection.md" >}}) | JWT protection allows you to configure policies based on properties of JSON web tokens, such as their header and signature properties. | +| [Override rules]({{< ref "/waf/policies/override-rules.md" >}}) | Override rules allow you to override default policy settings under specific conditions. | | [Server technology signatures]({{< ref "/waf/policies/server-technology-signatures.md" >}}) | Support adding signatures per added server technology. | | [Time-based signature staging]({{< ref "/waf/policies/time-based-signature-staging.md" >}}) | Time-based signature staging allows you to stage signatures for a specific period of time. During the staging period, violations of staged signatures are logged but not enforced. After the staging period ends, violations of staged signatures are enforced according to the policy's enforcement mode. | | [Threat campaigns]({{< ref "/waf/policies/threat-campaigns.md" >}}) | These are patterns that detect all the known attack campaigns. They are very accurate and have almost no false positives, but are very specific and do not detect malicious traffic that is not part of those campaigns. The default policy enables threat campaigns but it is possible to disable it through the respective violation. | | [User-defined HTTP headers]({{< ref "/waf/policies/user-headers.md" >}}) | Handling headers as a special part of requests | +| [User-defined URLs and parameters]({{< ref "/waf/policies/user-urls-parameters.md" >}}) | Use user-defined properties when configuring violations. | | [XFF trusted headers]({{< ref "/waf/policies/xff-headers.md" >}}) | Disabled by default, and can accept an optional list of custom XFF headers. | | [XML and JSON content]({{< ref "/waf/policies/xml-json-content.md" >}}) | XML content and JSON content profiles detect malformed content and signatures in the element values. Default policy checks maximum structure depth. It is possible to enable more size restrictions: maximum total length of XML/JSON data, maximum number of elements and more. | + {{< /table >}} diff --git a/content/waf/install/virtual-environment.md b/content/waf/install/virtual-environment.md index 724e90ca5..a1d4e10ed 100644 --- a/content/waf/install/virtual-environment.md +++ b/content/waf/install/virtual-environment.md @@ -162,10 +162,10 @@ Add F5 WAF for NGINX dependencies: sudo wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/dependencies.repo ``` -Enable the _codeready-builder_ repository: +Enable F5 WAF for NGINX dependencies: ```shell -sudo subscription-manager repos --enable codeready-builder-for-rhel-9-x86_64-rpms +sudo dnf config-manager --set-enabled crb ``` Install the F5 WAF for NGINX package and its dependencies: diff --git a/content/waf/logging/custom-dimensions.md b/content/waf/logging/custom-dimensions.md new file mode 100644 index 000000000..624fc8c67 --- /dev/null +++ b/content/waf/logging/custom-dimensions.md @@ -0,0 +1,100 @@ +--- +title: Custom dimensions for log entries +toc: false +weight: 200 +nd-content-type: reference +nd-product: NAP-WAF +--- + +Custom dimensions log entries feature refers to the new configuration in F5 WAF for NGINX, where the new directive called `app_protect_custom_log_attribute` is assigned to a particular location or server or http level in the `nginx.conf` file. The need is to be able to add custom identifiers to the respective location and/or server and identify requests in the Security Log by those identifiers. + +The `app_protect_custom_log_attribute` directive will be used to track the assigned location/server/http dimension of each request by adding the `app_protect_custom_log_attribute` to the **Security Logs** a.k.a **Request Logs**. Since it is a custom attribute a customer can set, that custom attribute will appear for every request log entry that was handled by that location/server. + +### Configuration + +A new directive `app_protect_custom_log_attribute` will be added to the `nginx.conf` file. You can set this directive at all scopes: http, server and location. The setting at the location scope overrides the setting in the server and/or http scopes and the server scope overrides the http scope. The `app_protect_custom_log_attribute` directive syntax will consist of a **name/value** or **key/value** pair i.e. "app_protect_custom_log_attribute ". + +Example Configuration: + +In the below example, we are configuring the `app_protect_custom_log_attribute` directive at the server and location level where we define the **key/value** pair as one string. + +```nginx + +user nginx; +load_module modules/ngx_http_app_protect_module.so; +error_log /var/log/nginx/error.log debug; + +events { + worker_connections 65536; +} +server { + + listen 80; + + server_name localhost; + proxy_http_version 1.1; + app_protect_custom_log_attribute ‘environment' 'env1'; + + location / { + + app_protect_enable on; + app_protect_custom_log_attribute gateway gway1; + app_protect_custom_log_attribute component comp1; + proxy_pass http://172.29.38.211:80$request_uri; + } + } +``` + +The **key/value** pair will be 'environment env1', ‘gateway gway1’ and ‘component comp1’ in the above examples, i.e. + +- app_protect_custom_log_attribute environment env1; +- app_protect_custom_log_attribute gateway gway1; +- app_protect_custom_log_attribute component comp1; + +The above key/value pair will be parsed as below: + +```shell +"customLogAttributes": [ + { + "name": "gateway", + "value": "gway1" + }, + { + "name": "component", + "value": "comp1" + }, +] +``` + +### Things to Remember While Configuring the Custom Dimensions Log Entries + +The `app_protect_custom_log_attribute` directive has a few limitations which should be kept in mind while configuring this directive: + +- Key and value strings are limited to 64 chars +- Maximum possible directive numbers are limited to 10 (in total) in each context i.e. Limit of 10 keys and values + +### Errors and Warnings + +An error message "`app_protect_custom_log_attribute` directive is invalid" will be displayed in the Security Log if the below conditions are met: + +1. If the `app_protect_custom_log_attribute` exceeds the maximum number of 10 directives +2. If the `app_protect_custom_log_attribute` exceeds the maximum name length of 64 chars +3. If the `app_protect_custom_log_attribute` exceeds the maximum value of 64 chars + +Error message example: + +```shell +app_protect_custom_log_attribute directive is invalid. Number of app_protect_custom_log_attribute directives exceeds maximum +``` + +### Logging and Reporting + +When `app_protect_custom_log_attribute` is assigned to a particular location/server/http context, it will appear in the `json_log` field as a new JSON property called "customLogAttributes" at the top level. The property will not appear if no `app_protect_custom_log_attribute` directive was assigned. + +Attributes at the http level applies to all servers and locations unless a specific server or location overrides the same key with a different value. Same goes for the server level and all locations under it. In the below example, the "environment" attribute will appear in logs of all locations under that server. + +Security logging example in json_log: + +```json +""customLogAttribute"":[{""name"":""component"",""value"":""comp1""},{""name"":""gateway"",""value"":""gway1""}]}" +``` \ No newline at end of file diff --git a/content/waf/logging/debug-logs.md b/content/waf/logging/debug-logs.md index 274b3cc87..e3bad3381 100644 --- a/content/waf/logging/debug-logs.md +++ b/content/waf/logging/debug-logs.md @@ -1,7 +1,7 @@ --- title: Debug logs toc: false -weight: 400 +weight: 500 nd-content-type: reference nd-product: NAP-WAF --- diff --git a/content/waf/logging/operation-logs.md b/content/waf/logging/operation-logs.md index 389081289..a3492eb88 100644 --- a/content/waf/logging/operation-logs.md +++ b/content/waf/logging/operation-logs.md @@ -1,7 +1,7 @@ --- title: Operation logs toc: false -weight: 300 +weight: 400 nd-content-type: reference nd-product: NAP-WAF --- diff --git a/content/waf/logging/security-logs.md b/content/waf/logging/security-logs.md index cc9744cb1..25ed5674b 100644 --- a/content/waf/logging/security-logs.md +++ b/content/waf/logging/security-logs.md @@ -1,7 +1,7 @@ --- title: Security logs toc: true -weight: 200 +weight: 300 nd-content-type: reference nd-product: NAP-WAF --- diff --git a/content/waf/policies/_index.md b/content/waf/policies/_index.md index 1c0cce495..7173947ab 100644 --- a/content/waf/policies/_index.md +++ b/content/waf/policies/_index.md @@ -1,5 +1,11 @@ --- title: "Policies" url: /waf/policies/ +cascade: + nd-banner: + enabled: true + type: deprecation + start-date: 2025-09-30 + md: /_banners/waf-parameter-reference.md weight: 400 --- diff --git a/content/waf/policies/attack-signatures.md b/content/waf/policies/attack-signatures.md index a6dca026f..36ca18bb0 100644 --- a/content/waf/policies/attack-signatures.md +++ b/content/waf/policies/attack-signatures.md @@ -203,7 +203,7 @@ To exclude multiple attack signatures, each signature ID needs to be added as a In the previous examples, the signatures were disabled for all the requests that are inspected by the respective policy. You can also exclude signatures for specific URLs or parameters, while still enable them for the other URLs and parameters. -The topics [User-defined URLs]() and [User-defined parameters]() have more details. +The topic [User-defined URLs and parameters]({{< ref "/waf/policies/user-urls-parameters.md" >}}) has more details. In some cases, you may want to remove a whole signature set that was included in the default policy. For example, a protected application may not use XML and is not vulnerable to XPath injection. diff --git a/content/waf/policies/directives.md b/content/waf/policies/directives.md new file mode 100644 index 000000000..bb071a5f4 --- /dev/null +++ b/content/waf/policies/directives.md @@ -0,0 +1,137 @@ +--- +# We use sentence case and present imperative tone +title: "Directives" +# Weights are assigned in increments of 100: determines sorting order +weight: 150 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- + +This topic describes the global NGINX directives and directives specific to F5 WAF for NGINX. + + +## Global directives + +Global configuration consists of a series of `nginx.conf` directives at the `http` context controlling aspects that are not specific to a specific application. When applied to a cluster, all cluster members will get the same globals. + +The URL in a request determines whether or not it will be inspected by F5 WAF for NGINX. This is defined by `app_protect_enable` and `app_protect_policy_file` directives in the `location` scope. + +In the case that the URL itself has violations such as *bad unescape* or *illegal metacharacter*, the request may be assigned to a location in which F5 WAF for NGINX is disabled or has a relaxed policy that does not detect these violations. + +Such malicious requests will be allowed without inspection. + +In order to avoid this, it is recommended to have a basic policy enabled at the `http` scope or at least at the `server` scope to process malicious requests in a more complete manner. + +{{< table >}} + +| Name | Syntax | Description | Default | +| ---- | -------| ----------- | ------- | +| app_protect_physical_memory_util_thresholds | app_protect_physical_memory_util_thresholds high= low= | Sets the physical memory utilization thresholds for entering (high) and exiting (low) failure mode. When the high threshold is exceeded the system enters failure mode until memory drops below the low threshold. Setting the value of 100 disables this feature. | high=low=100 (disabled) | +| app_protect_cpu_thresholds | app_protect_cpu_thresholds high= low= | Sets the CPU utilization thresholds for entering and exiting failure mode respectively: when the high threshold is exceeded the system enters failure mode until CPU drops below the low threshold. Setting the value of 100 disables this feature.
**Note**: The system does not enter failure mode during policy compilation after reload even if the threshold is exceeded. | high=low=100 (disabled) | +| app_protect_failure_mode_action | app_protect_failure_mode_action pass | drop | How to handle requests when the F5 WAF for NGINX Enforcer cannot process them, either because it is down, disconnected or because of excessive CPU or memory utilization. There are two values:
  • **pass**: Pass the request without F5 WAF for NGINX Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "503 Service Unavailable", a.k.a. "fail-close".
| pass | +| app_protect_cookie_seed | app_protect_cookie_seed | A long randomized string that serves to generate the encryption key for the cookies generated by F5 WAF for NGINX. The string should contain only alphanumeric characters and be no longer than 1000 characters. | Auto-generated random string | +| app_protect_compressed_requests_action | app_protect_compressed_requests_action pass | drop | Determines how to handle compressed requests. There are two values:
  • **pass**: Pass the request without F5 WAF for NGINX Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "501 Not Implemented", a.k.a. "fail-close".
**Note**: Starting with F5 WAF for NGINX release version 4.6, this directive has been deprecated from the `nginx.conf` file. | drop | +| app_protect_request_buffer_overflow_action | app_protect_request_buffer_overflow_action pass | drop | Determines how to handle requests in case the NGINX request buffer is full and requests cannot be buffered anymore. There are two values:
  • **pass**: Pass the request without F5 WAF for NGINX Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by resetting connection. No response page is returned, a.k.a. "fail-close".
| pass | +| app_protect_user_defined_signatures | app_protect_user_defined_signatures | Imports the user-defined tagged signature file with the respective tag name from the provided path. Multiple instances of this directive are supported. In order to import multiple signatures files, each file must have a different tag. | N/A | +|app_protect_reconnect_period_seconds| app_protect_reconnect_period_seconds
**Value type**: number with decimal fraction
**Value Range**: 0-60. 0 is illegal | Determines the period of time between reconnect retries of the module to the web application firewall (WAF) engine. The time unit is seconds.| 5 | + +{{< /table >}} + +## F5 WAF for NGINX directives + +{{< table >}} + +| Name | Syntax | Functionality | Contexts | Example | +| ---- | ------ | ------------- | -------- | ------- | +| load_module | load_module | NGINX directive to load the F5 WAF for NGINX module. It must be invoked with the F5 WAF for NGINX library path | Global | load_module modules/ngx_http_app_protect_module.so | +| app_protect_enable | app_protect_enable on | off | Whether to enable F5 WAF for NGINX at the respective context. If not present, inherits from the parent context | HTTP, Server, Location | app_protect_enable on | +| app_protect_policy_file | app_protect_policy_file | Set a F5 WAF for NGINX policy configuring behavior for the respective context. | HTTP, Server, Location | app_protect_policy_file /config/waf/strict_policy.json | +| app_protect_security_log_enable | app_protect_security_log_enable on | off | Whether to enable the F5 WAF for NGINX per-request log at the respective context. | HTTP, Server, Location | app_protect_security_log_enable on | +| app_protect_security_log | app_protect_security_log | Specifies the per-request logging: what to log and where | HTTP, Server, Location | app_protect_security_log /config/waf/log_illegal.json syslog:localhost:522 | +| app_protect_custom_log_attribute | app_protect_custom_log_attribute | Specifies the assigned location/server/http dimension of each request. | HTTP, Server, Location | app_protect_custom_log_attribute ‘environment' 'env1' | + +{{< /table >}} + +### Horizontal scaling + +F5 WAF for NGINX can be deployed in multiple instances that share the traffic to the same applications. + +In this case, all instances must share the same configuration files. + +It is your responsibility to synchronize the files on all instances. You must also load balancing each of those instances, such as using additional NGINX instances. + +When deploying multiple scalability instances add the `app_protect_cookie_seed` directive to nginx.conf in the `http` block: + +```nginx +... +http { + ... + app_protect_cookie_seed jkldsf90upiokasdj120; + ... + server { + listen 80; +... +} +... +``` + +The argument for the directive should be a random alphanumeric string of at least 20 characters length (Maximum 1000 characters). + +This is a seed used by F5 WAF for NGINX to generate the encryption key for the cookies it creates. These cookies are used for various purposes such as validating the integrity of the cookies generated by the application. + +In the absence of this directive, F5 WAF for NGINX generates a random string by itself. In that case, each instance will have a different seed. + +A cookie created and encrypted on one instance of F5 WAF for NGINX will fail to be decrypted when sent by the same client to another F5 WAF for NGINX instance having a different encryption key. + +### Failure modes + +If the F5 WAF for NGINX daemons are down or disconnected from the NGINX workers, there are two modes of operation until they are up and connected again: + +- **Pass** the traffic without inspection. Use this when preferring availability over security. This mode is also known as "fail open". +- **Drop** the traffic. Use this when preferring security over availability. This mode is also known as "fail closed". + +The default is to **pass**, fail open, but you can control this using the `app_protect_failure_mode_action` directive with one argument with two possible values: "pass" or "fail" for the two above options. + +This directive is also placed in the `http` block of the nginx.conf file. + +```nginx +... +http { + ... + app_protect_failure_mode_action drop; + ... + server { + listen 80; +... + } +... +``` + +## Handling compressed requests + +{{< call-out "warning" >}} + +From F5 WAF for NGINX release version 4.6, the `app_protect_compressed_requests_action` directive was removed deprecated from the nginx configuration. + +When configuring this directive in the `nginx.conf` file, F5 WAF for NGINX will disregard any previously used values ("pass" or "drop") and issue a warning. + +{{< /call-out >}} + +By default, the enforcer will now decompress all the HTTP compressed payload request and will apply the enforcment. + +The supported compression algorithms for this feature are "**gzip**" and "**deflate**". + +Decompression may fail under certain conditions: + +- If the compression method is not supported +- If the 'Content-Encoding' header does not match the compression algorithm used while sending a compressed payload in a HTTP request +- If thehe decompressed request is larger than 10 MB + +If it does exceed this limit, F5 WAF for NGINX will only decompress the first 10 KB, ignoring the remainder, and trigger the `VIOL_REQUEST_MAX_LENGTH` violation, just as it would for an uncompressed request that exceeds 10 MB. + +In the cases where decompression fails, F5 WAF for NGINX will continue with the scan in the same manner as it does for uncompressed requests. \ No newline at end of file diff --git a/content/waf/policies/do-nothing.md b/content/waf/policies/do-nothing.md new file mode 100644 index 000000000..92d87b7ea --- /dev/null +++ b/content/waf/policies/do-nothing.md @@ -0,0 +1,43 @@ +--- +# We use sentence case and present imperative tone +title: "Do-nothing" +# Weights are assigned in increments of 100: determines sorting order +weight: 1050 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- + +This topic describes the do-nothing policy feature of F5 WAF for NGINX. + +Within _urlContentProfiles_, adding the _do-nothing_ type allows the user to avoid inspecting or parsing the content in a policy, and instead handle the request's header according to the specifications outlined in the security policy. + +The following example configures do-nothing for a specific user-defined URL: + +```json +{ + "policy" : { + "name": "ignore_body", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "urls": [ + { + "method": "*", + "name": "*", + "type": "wildcard", + "urlContentProfiles": [ + { + "headerName": "*", + "headerOrder": "default", + "headerValue": "*", + "type": "do-nothing" + } + ] + } + ] + } +} +``` \ No newline at end of file diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md new file mode 100644 index 000000000..07fb0a6bb --- /dev/null +++ b/content/waf/policies/external-references.md @@ -0,0 +1,563 @@ +--- +# We use sentence case and present imperative tone +title: "External references" +# Weights are assigned in increments of 100: determines sorting order +weight: 200 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- + +This topic describes the external references feature for F5 WAF for NGINX. + +External references in policy are code blocks that can be used as part of a policy without being part of the policy file. This means that you can have a set of pre-defined configurations for parts of the policy, and you can incorporate them as part of the policy by simply referencing them. + +It allows you to separate policy information into smaller files, which can be easier to maintain for a large, complex policy. Another use case for external references is to build a dynamic policy that requires replaceable files. + +You can create and populate specific files with the configuration relevant to your policy, and then compile the policy to include the latest version of these files, ensuring that your policy is always up to date when it comes to a constantly changing environment. + +{{< call-out "note" >}} + +Updating a single file referenced in the policy will not trigger a policy compilation. This action needs to be done actively by reloading the NGINX configuration. + +{{< /call-out >}} + +To use external references, replaced the direct property in the policy file with the _\Reference_ property, where _\_ defines the replacement text for the property changed to singular (if originally plural) and notation converted from snake case to camelCase. + +For example, a `modifications` section could be replaced by `modificationsReference` and `data-guard` would could be replaced by `dataGuardReference`. + +## External reference types + +There are different implementations based on the type of references that are being made. + +### External URL reference + +URL reference is the method of referencing an external source by providing its full URL. + +This is a useful method when trying to combine or consolidate parts of the policy that are present on different host machines. + +{{< call-out "note" >}} + +You need to make sure that the server where the resource files are located is always available when you are compiling your policy. +{{< /call-out >}} + +This example creates a skeleton policy, enabling the file type violation. + +It does not specify the file types as these file types depend on a separate application that defines these types. It populates this section from an external reference instead. + +Note that the `filetypes` section is replaced by the `filetypeReference` section. In the `filetypeReference` section, there is **key/value** pair defining the type of reference and actual URL to use to reach that reference item. + +For the content of the file itself, it is an extension of the original JSON format for the policy. + +```json +{ + "name": "external_resources_file_types", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_FILETYPE", + "alarm": true, + "block": true + } + ] + }, + "filetypeReference": { + "link": "http://domain.com:8081/file-types.txt" + } +} +``` + +The referenced `file-types.txt` file contains the following code: + +```json +[ + { + "name": "*", + "type": "wildcard", + "allowed": true, + "checkPostDataLength": false, + "postDataLength": 4096, + "checkRequestLength": false, + "requestLength": 8192, + "checkUrlLength": true, + "urlLength": 2048, + "checkQueryStringLength": true, + "queryStringLength": 2048, + "responseCheck": false + }, + { + "name": "pat", + "allowed": false + }, + { + "name": "mat", + "allowed": false + } +] +``` + +### HTTPS references + +HTTPS references are a special case of URL references. It uses the HTTPS protocol instead of the HTTP protocol. Make sure that the webserver you are downloading the resources from does also support HTTPS protocol and has certificates setup properly. + +- Certificates must be valid in date (not expired) during the policy compilation. +- Certificates must be signed by a trusted CA. +- For Self-signed certificates, you need to make sure to add your certificates to the trusted CA of the machine where App Protect is installed. +- Certificates must use the exact domain name that the certificate was issued for. For example, SSL will differentiate between domain.com and www.domain.com, considering each a different domain name. + +In this configuration, we are completely satisfied with the basic default policy, and we wish to use it as is. However, we wish to define a custom response page using an external file located on an HTTPS web server. The external reference file contains our custom response page configuration. + +**Policy configuration:** + +```json +{ + "name": "external_references_custom_respsonse", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "responsePageReference": { + "link": "https://securedomain.com:8081/response-pages.txt" + } +} +``` + +Content of the referenced file `response-pages.txt`: + +```json +[ + { + "responseContent": "Custom Reject PageThis is a custom response page, it is supposed to overwrite the default page with custom text.

Your support ID is: <%TS.request.ID()%>

[Go Back]", + "responseHeader": "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nConnection: close", + "responseActionType": "custom", + "responsePageType": "default" + } +] +``` + +In this example, we would like to enable all attack signatures. Yet, we want to exclude specific signatures from being enforced. + +**Policy configuration:** + +```json +{ + "policy": { + "name": "external_resources_signature_modification", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ] + }, + "modificationsReference": { + "link": "http://my-domain.com:8081/modifications.txt" + } +} +``` + +Content of the referenced file `modifications.txt`: + +```json +{ + "modifications": [ + { + "entityChanges": { + "enabled": false + }, + "entity": { + "signatureId": 200001834 + }, + "entityType": "signature", + "action": "add-or-update" + } + ] +} +``` + +### File references + +File references refers to accessing local resources on the same machine, as opposed to accessing a remote resource on another server/machine. The user can specify any location that is accessible by App Protect except for the root folder ("/"). If no full path is provided, the default path `/etc/app_protect/conf` will be assumed. Note that file references can only be on the local machine: you cannot use remote hosts! + +Here are some examples of the typical cases: + +| Examples | File path | Notes | +| -------- | --------- | ----- | +| | /etc/app_protect/conf/foo.json | Default directory assumed | +| | /etc/app_protect/conf/foo.json | Formally illegal, but tolerated as long as there is no trailing slash. | +| | /etc/app_protect/conf/foo.json | Full path, but still the default one | +| | /bar/foo.json | Non-default path | +| | **Not accepted** | "etc" is interpreted as remote host name | + + +In this example, we would like to enable all attack signatures. Yet, we want to exclude specific signatures from being enforced. To do this, we reference a local file on the machine. + +**Policy Configuration:** + +```json +{ + "policy": { + "name": "external_resources_signature_modification", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ] + }, + "modificationsReference": { + "link": "file:///modifications.txt" + } +} +``` + +Content of the referenced file `modifications.txt`: + +```json +{ + "modifications": [ + { + "entityChanges": { + "enabled": false + }, + "entity": { + "signatureId": 200001834 + }, + "entityType": "signature", + "action": "add-or-update" + } + ] +} +``` + +If, for any reason, the configuration was done incorrectly, the policy compilation process will fail with the following error: +```shell +APP_PROTECT { "event": "configuration_load_failure" ... +``` + +The error details that follow will depend on the exact situation causing the policy compilation to fail. If the policy compilation process fails, the compiler will revert to the last working policy and all the changes for the last policy compilation attempt will be lost. + +## OpenAPI specification file reference + +The OpenAPI Specification defines the spec file format needed to describe RESTful APIs. The spec file can be written either in JSON or YAML. Using a spec file simplifies the work of implementing API protection. Refer to the OpenAPI Specification (formerly called Swagger) for details. + +The simplest way to create an API protection policy is using an OpenAPI Specification file to import the details of the APIs. If you use an OpenAPI Specification file, F5 WAF for NGINX will automatically create a policy for the following properties (depending on what's included in the spec file): +* Methods +* URLs +* Parameters +* JSON profiles + +An OpenAPI-ready policy template is provided with the F5 WAF for NGINX packages and is located in: `/etc/app_protect/conf/NginxApiSecurityPolicy.json` + +It contains violations related to OpenAPI set to blocking (enforced). + +{{< call-out "note" >}} F5 WAF for NGINX supports only one OpenAPI Specification file reference per policy.{{< /call-out >}} + +### Types of OpenAPI References + +There are different ways of referencing OpenAPI Specification files. The configuration is similar to [External References](#external-references). + +{{< call-out "note" >}} Any update of an OpenAPI Specification file referenced in the policy will not trigger a policy compilation. This action needs to be done actively by reloading the NGINX configuration. {{< /call-out >}} + +#### URL Reference + +URL reference is the method of referencing an external source by providing its full URL. + +Make sure to configure certificates prior to using the HTTPS protocol - see the [HTTPS References](#https-reference) under the External References section for more details. + +{{< call-out "note" >}} You need to make sure that the server where the resource files are located is always available when you are compiling your policy. {{< /call-out >}} + +##### Example Configuration + +In this example, we are adding an OpenAPI Specification file reference using the link `http://127.0.0.1:8088/myapi.yaml`. This will configure allowed data types for `query_int` and `query_str` parameters values. + +**Policy configuration:** + +```json +{ + "policy": { + "name": "petstore_api_security_policy", + "description": "F5 WAF for NGINX API Security Policy for the Petstore API", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "open-api-files": [ + { + "link": "http://127.0.0.1:8088/myapi.yaml" + } + ], + "blocking-settings": { + "violations": [ + { + "block": true, + "description": "Disallowed file upload content detected in body", + "name": "VIOL_FILE_UPLOAD_IN_BODY" + }, + { + "block": true, + "description": "Mandatory request body is missing", + "name": "VIOL_MANDATORY_REQUEST_BODY" + }, + { + "block": true, + "description": "Illegal parameter location", + "name": "VIOL_PARAMETER_LOCATION" + }, + { + "block": true, + "description": "Mandatory parameter is missing", + "name": "VIOL_MANDATORY_PARAMETER" + }, + { + "block": true, + "description": "JSON data does not comply with JSON schema", + "name": "VIOL_JSON_SCHEMA" + }, + { + "block": true, + "description": "Illegal parameter array value", + "name": "VIOL_PARAMETER_ARRAY_VALUE" + }, + { + "block": true, + "description": "Illegal Base64 value", + "name": "VIOL_PARAMETER_VALUE_BASE64" + }, + { + "block": true, + "description": "Disallowed file upload content detected", + "name": "VIOL_FILE_UPLOAD" + }, + { + "block": true, + "description": "Illegal request content type", + "name": "VIOL_URL_CONTENT_TYPE" + }, + { + "block": true, + "description": "Illegal static parameter value", + "name": "VIOL_PARAMETER_STATIC_VALUE" + }, + { + "block": true, + "description": "Illegal parameter value length", + "name": "VIOL_PARAMETER_VALUE_LENGTH" + }, + { + "block": true, + "description": "Illegal parameter data type", + "name": "VIOL_PARAMETER_DATA_TYPE" + }, + { + "block": true, + "description": "Illegal parameter numeric value", + "name": "VIOL_PARAMETER_NUMERIC_VALUE" + }, + { + "block": true, + "description": "Parameter value does not comply with regular expression", + "name": "VIOL_PARAMETER_VALUE_REGEXP" + }, + { + "block": true, + "description": "Illegal URL", + "name": "VIOL_URL" + }, + { + "block": true, + "description": "Illegal parameter", + "name": "VIOL_PARAMETER" + }, + { + "block": true, + "description": "Illegal empty parameter value", + "name": "VIOL_PARAMETER_EMPTY_VALUE" + }, + { + "block": true, + "description": "Illegal repeated parameter name", + "name": "VIOL_PARAMETER_REPEATED" + } + ] + } + } +} +``` + +Content of the referenced file `myapi.yaml`: + +```yaml +openapi: 3.0.1 +info: + title: 'Primitive data types' + description: 'Primitive data types.' + version: '2.5.0' +servers: + - url: http://localhost +paths: + /query: + get: + tags: + - query_int_str + description: query_int_str + operationId: query_int_str + parameters: + - name: query_int + in: query + required: false + allowEmptyValue: false + schema: + type: integer + - name: query_str + in: query + required: false + allowEmptyValue: true + schema: + type: string + responses: + 200: + description: OK + 404: + description: NotFound +``` + +In this case the following request will trigger an `Illegal parameter data type` violation, as we expect to have an integer value in the `query_int` parameter: + +``` +http://localhost/query?query_int=abc +``` + +The request will be blocked. + +The link option is also available in the `openApiFileReference` property and synonymous with the one above in `open-api-files` + +**Note**: `openApiFileReference` is not an array. + +##### Example Configuration + +In this example, we reference the same OpenAPI Specification file as in the policy above using the `openApiFileReference` property. + +**Policy configuration:** + +```json +{ + "name": "openapifilereference-yaml", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "openApiFileReference": { + "link": "http://127.0.0.1:8088/ref.txt" + } +} +``` + +Content of the file `ref.txt`: + +```json +[ + { + "link": "http://127.0.0.1:8088/myapi.yaml" + } +] +``` + +#### File Reference + +File reference refers to accessing local resources on the same machine. See the [File References](#file-reference) under the External References section for more details. + +##### Example Configuration + +In this example, we would like to add an OpenAPI Specification file reference to the default policy. + +**Policy Configuration:** + +```json +{ + "name": "openapi-file-reference-json", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "open-api-files": [ + { + "link": "file:///myapi2.json" + } + ] +} +``` + +Content of the referenced file `myapi2.json`: + +```json +{ + "openapi": "3.0.1", + "info": { + "title": "Primitive data types2", + "description": "Primitive data types.", + "version": "2.5.1" + }, + "servers": [ + { + "url": "http://localhost" + } + ], + "paths": { + "/query": { + "get": { + "tags": [ + "query_bool" + ], + "description": "query_bool", + "operationId": "query_bool", + "parameters": [ + { + "name": "query_bool", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "NotFound" + } + } + } + } + } +} +``` + +In this case the following request will trigger an `Illegal repeated parameter name` violation, as the OpenAPI Specification doesn't allow repeated parameters. + +``` +http://localhost/query?a=true&a=false +``` + +The request will not be blocked because this violation is set to alarm in the default policy. \ No newline at end of file diff --git a/content/waf/policies/jwt-protection.md b/content/waf/policies/jwt-protection.md index 8edb1239d..35b0b043f 100644 --- a/content/waf/policies/jwt-protection.md +++ b/content/waf/policies/jwt-protection.md @@ -1,6 +1,6 @@ --- title: JWT protection -weight: 1650 +weight: 1700 toc: true nd-content-type: reference nd-product: NAP-WAF diff --git a/content/waf/policies/override-rules.md b/content/waf/policies/override-rules.md new file mode 100644 index 000000000..db0f18e04 --- /dev/null +++ b/content/waf/policies/override-rules.md @@ -0,0 +1,263 @@ +--- +title: Override rules +weight: 1800 +toc: true +nd-content-type: reference +nd-product: NAP-WAF +nd-docs: DOCS-000 +--- + +This topic describes override rules feature of F5 WAF for NGINX. + +Override rules allow you to replace **default policy** settings. These override rules can be included as part of a declarative policy so that all incoming requests are verified against those rules. + +Each override rule consists of a condition followed by changes to the original policy which will apply to requests that meet the condition. + +You can apply this feature to apply unique policy settings to incoming requests with one or more rules based on multiple parameters: + +- Method +- Source IP address +- Header values +- URI values + +To ensure efficient compilation time and optimal resource allocation for policies, policies have a maximum limit of 10 rules and a maximum of 5 clauses in a condition. A compilation error will not occur if a policy file contains more than 5 clauses or 10 overrides. + +The replacement policy should not include any override rules. Override rules should be used to extend or switch to a different policy, rather than being part of the replacement policy itself. + +Each override rule is compiled as a separate policy, whether extending the main policy or switching to a new one. The enforcer will switch to the policy that corresponds to the matched rule, but the main policy name will be reported along with the override rule property. + +The URI, host, and user-agent strings in the request are treated as plain ASCII characters and won't undergo language decoding. If any of these strings contain non-ASCII characters, they may be misinterpreted and may not comply with rules that expect specific values in the conditions. + +The structure of these override rules adheres to the JSON schema defined by the declarative policy. + +An example could look like the following: + +```shell +{ + "policy": { + "name": "override_rules_example", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "override-rules": [ + { + "name": "localhost-log-only", + "condition": "host.contains('localhost') and clientIp == '127.0.0.1' and userAgent.lower().startsWith('curl')", + "override": { + "policy": { + "enforcementMode": "transparent" + } + } + }, + { + "name": "login_page", + "condition": "method == 'POST' and not parameters['ref'].lower().matches('example') and uri.contains('/login/')", + "actionType": "replace-policy", + "override": { + "policy": { + "name": "login_page_block_redirect", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ], + "response-pages": [ + { + "responseRedirectUrl": "https://example.com/rejected?id=<%TS.request.ID()%>", + "responseActionType": "redirect", + "responsePageType": "default" + } + ] + } + } + }, + { + "name": "api-strict", + "condition": "uri.contains('api4') and not (clientIp.matches('fd00:1::/48') or userAgent.lower().startsWith('Mozilla'))", + "actionType": "replace-policy", + "override": { + "$ref": "file:///NginxStrictPolicy.json" + } + }, + { + "name": "strict-post", + "condition": "method.matches('POST') and (cookies['sessionToken'] != 'c2Vzc2lvblRva2Vu' or headers['Content-Encoding'] == 'gzip')", + "actionType": "replace-policy", + "override": { + "$ref": "file:///NginxStrictPolicy.json" + } + }, + "name": "usa-only", + "condition": "geolocation != 'US'", + "actionType": "violation", + "violation": { + "block": true, + "alarm": true, + "attackType": { + "name": "Forceful Browsing" + }, + "description": "Attempt to access from outside the USA", + "rating": 4 + } + } + ] + } +} +``` + +The _override_rules_example_ example policy contains five override rules: + +1. The **"localhost-log-only"** rule applies to the requests with a user agent header starting with "curl", a host header containing "localhost", and a client IP address set to 127.0.0.1. It switches the enforcement mode to "transparent" without blocking the request. The remaining policy settings remain unchanged. This type of override rule is an example of an **Inline Policy Reference**. +1. The **"login_page"** rule is triggered by POST requests to URIs containing "/login/". Since the "actionType" field is set to "replace-policy", it overrides the policy with a new one named "login_page_block_redirect". This new policy is independent of the "override_rules_example" policy. It enables all signature sets and redirects the user to a rejection page. This is another example of an **Inline Policy Reference** with a different condition. +1. The **"api-strict"** rule is applied for requests with "api4" in the URI, except for client IP addresses matching the "fd00:1::/48" range and user agents starting with "Mozilla". It references an external policy file named "NginxStrictPolicy.json" located at "/etc/app_protect/conf/" to override the current policy. The "actionType" field is set to "replace-policy" and the external policy can be specified using a reference to its file using **$ref**. The file is the JSON policy source of that policy. This type of policy switching is known as **External Policy Reference**. +1. The **"strict-post"** rule is triggered when POST requests include a session token in the cookies that is not equal to "c2Vzc2lvblRva2Vu" or when the "gzip" value is found in the content-encoding headers. This rule follows a similar approach to referencing an external policy file, just like the **api-strict** rule mentioned above. +1. The **"usa-only"** rule is triggered when a request coming from a country other than the USA. The actionType is set to "violation", meaning that `VIOL_RULE` violation is triggered for such a request. This violation will block and mark the request as illegal with regard to the "block" and "alarm" attributes. There is no change in policy for this rule. + +These five rules demonstrate how the override rules feature allows for customization and the ability to modify specific aspects of the original policy based on predefined conditions. + +For more details about the **Geolocation** feature, view the [Geolocation]({{< ref "/waf/policies/geolocation.md" >}}) topic. + +{{< call-out "note" >}} + +By default, the actionType field is configured to "extend-policy". + +External references are supported for any policy reference. + +{{< /call-out >}} + +### First match principle + +Policy enforcement operates on the **first match** principle. This principle is applied when multiple conditions match or are similar, indicating that any incoming requests that match the first condition will be processed. + +In the following example, the _override_rules_example2_ policy uses two override rules: `this_rule_will_match` and `non_matching_rule`. + +Since both conditions match, the first match principle will be applied, and requests with "_api_" in the URI will be processed. + +It will reference the external policy file named _NginxStrictPolicy.json_ to override the current policy. + +```shell +{ + "policy": { + "name": "override_rules_example2", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "override-rules": [ + { + "name": "this_rule_will_match", + "condition": "uri.contains('api')", + "actionType": "replace-policy", + "override": { + "$ref": "file:///NginxStrictPolicy.json" + } + }, + { + "name": "non_matching_rule", + "condition": "uri.contains('api') and not clientIp == '192.168.0.10'", + "actionType": "extend-policy", + "override": { + "policy": { + "enforcementMode": "transparent" + } + } + } + ] + } +} +``` + +### Logging & reporting override rules + +If a request matches an override rule, the `json_log` field will include a new block named 'overrideRule'. However, if no rules match the request, the log will not contain any related information. + +When the 'actionType' flag is set to _replace-policy_, the _originalPolicyName_ field in the log will reflect the name of the original policy name (Which contains override rules), and the `policy_name` field will reflect the policy that was enforced. + +For example, if the matching override rule is called "login_page": + +```shell +policy_name="login_page_block_redirect" +``` + +_json_log_ will have: + +```JSON +{ + ... + "overrideRule": { + "name": "login_page", + "originalPolicyName": "override_rule_example" +} + ... + +``` + +If the matching override rule is called _usa-only_: + +```shell +{ +... + "enforcementState": { + "isBlocked": true, + "isAlarmed": true, + "rating": 4, + "attackType": [ + { + "name": "ATTACK_TYPE_FORCEFUL_BROWSING" + } + ] + }, + "violation": { + "name": "VIOL_RULE" + }, + "policyEntity": { + "override-rule": { + "name": "usa-only" + } + }, + "description": "Trying to access special" +}, +... +``` + +### Errors and warnings + +#### Missing policy name + +Every policy must have a name if the actionType is `extend-policy` or `replace-policy`. + +If the policy `name` is not provided in the override section, an error message will be displayed indicating the missing policy 'name' within that specific override rule. + +For instance, in the override rule below, the policy name is not specified. + + +```shell +"override-rules": [ + { + "name": "example-rule", + "condition": "uri.contains('127')", + "actionType": "replace-policy", + "override": { + "policy": { + "name": "policy_name", <--- the missing part + "enforcementMode": "transparent" + } + } + } +] +``` +```shell +"error_message": "Failed to import Policy 'policy1' from '/etc/app_protect/conf/test.json': Missing policy 'name' in the override rule 'example-rule'." +``` + +#### Cyclic override rule error + +If an inline or externally referenced policy contains an override rule, a cyclic override rule error will be issued. + +```shell +"error_message": "Failed to import an override policy: Cyclic override-rules detected." +``` \ No newline at end of file diff --git a/content/waf/policies/user-urls-parameters.md b/content/waf/policies/user-urls-parameters.md new file mode 100644 index 000000000..7875cf5f1 --- /dev/null +++ b/content/waf/policies/user-urls-parameters.md @@ -0,0 +1,616 @@ +--- +# We use sentence case and present imperative tone +title: "User-defined URLs and parameters" +# Weights are assigned in increments of 100: determines sorting order +weight: 2150 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- + +This topic describes the user-defined URLs and parameters feature of F5 WAF for NGINX. + +## URLs + +User-defined URLs allows you to configure a URL with the following options: +- Define a protected URL configuration with an explicitly path or implicit wildcard +- Define a list of allowed/disallowed methods per URL that will override the list defined in the policy level. +- Define the content-types `json/xml/form-data` for a user-defined URL. +- Define an Allowed/Disallowed for user-defined URL. +- Add user-defined URLs to the Signature/Metacharacters override list. + +For `urlContentProfiles` default values, see F5 WAF for NGINX [Declarative Policy guide.]({{< ref "/nap-waf/v4/declarative-policy/policy.md" >}}) + +This example configures allowed meta-characters for a user-defined URL: + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + }, + { + "name": "VIOL_URL_METACHAR", + "alarm": true, + "block": true + } + ] + }, + "urls": [ + { + "method": "*", + "name": "/meta*", + "protocol": "http", + "type": "wildcard", + "metacharsOnUrlCheck": true, + "metacharOverrides": [ + { + "isAllowed": true, + "metachar": "0x3c" + }, + { + "isAllowed": false, + "metachar": "0x28" + } + ], + "wildcardOrder": 2 + } + ] + } +} +``` + +This next example disables the detection of a specific signature (`200010093`) and enables another (`200010008`) for the user-defined URL `/Common/user_defined_URL`. + +These signature settings take effect only in requests to that URL. + +For other requests, the signature behavior is determined by the signature sets these signatures belong to. View [signature sets]({{< ref "/waf/policies/attack-signatures.md#signature-sets" >}}) for more details. + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + } + ] + }, + "urls": [ + { + "method": "*", + "name": "/test*", + "protocol": "http", + "type": "wildcard", + "wildcardOrder": 1, + "attackSignaturesCheck": true, + "signatureOverrides": [ + { + "enabled": true, + "signatureId": 200010008 + }, + { + "enabled": false, + "signatureId": 200010093 + } + ] + } + ] + } +} +``` + +This example shows wildcard and explicit URL configuration, where the first URL is permitted for all methods, and the second is permitted only for GET: + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + } + ] + }, + "urls": [ + { + "method": "*", + "name": "/test*", + "protocol": "http", + "type": "wildcard", + "wildcardOrder": 1 + }, + { + "method": "GET", + "name": "/index.html", + "protocol": "http", + "type": "explicit" + } + ] + } +} +``` + +This final example shows how to configure json/xml/form-data content types for a specific user-defined URL: + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + }, + { + "name": "VIOL_METHOD", + "alarm": true, + "block": true + }, + { + "name": "VIOL_JSON_MALFORMED", + "alarm": true, + "block": true + }, + { + "name": "VIOL_JSON_FORMAT", + "alarm": true, + "block": true + }, + { + "name": "VIOL_XML_FORMAT", + "alarm": true, + "block": true + } + ] + }, + "json-profiles": [ + { + "name": "Default", + "handleJsonValuesAsParameters": false, + "defenseAttributes": { + "maximumTotalLengthOfJSONData": "any", + "maximumArrayLength": "any", + "maximumStructureDepth": "any", + "maximumValueLength": "any" + } + } + ], + "xml-profiles": [ + { + "name": "Default", + "defenseAttributes": { + "maximumAttributesPerElement": "any", + "maximumDocumentDepth": "any", + "maximumAttributeValueLength": "any", + "maximumChildrenPerElement": "any", + "maximumDocumentSize": "any", + "maximumElements": "any", + "maximumNameLength": "any", + "maximumNSDeclarations": "any", + "maximumNamespaceLength": "any", + "tolerateLeadingWhiteSpace": true, + "tolerateCloseTagShorthand": true, + "allowCDATA": true, + "allowExternalReferences": true, + "allowProcessingInstructions": true + } + } + ], + "urls": [ + { + "method": "*", + "name": "/first*", + "protocol": "http", + "type": "wildcard", + "wildcardOrder": 1, + "urlContentProfiles": [ + { + "headerValue": "*", + "headerName": "*", + "headerOrder": "3", + "type": "form-data" + }, + { + "contentProfile": { + "name": "Default" + }, + "headerValue": "*xml*", + "headerName": "Content-Type", + "headerOrder": "2", + "type": "xml" + }, + { + "contentProfile": { + "name": "Default" + }, + "headerValue": "*json*", + "headerName": "Content-Type", + "headerOrder": "1", + "type": "json" + } + ] + } + ] + } +} +``` + +## Parameters + +User-defined parameters allow you to give specific attributes to specific parameters. + +This feature gives you full control over what the parameter should include and where it should be located, allowing for granularity to configure every parameter. + +With user-defined parameters you can:: + +- Create unique parameters and specify attributes for each +- Define what data type the parameter should contain +- Define the allowed location where you expect to see a parameter +- Define minimum/maximum values and minimum/maximum lengths for a parameter +- Define whether a parameter is mandatory or not +- Define whether the parameter can have empty values or not +- Define whether to inspect a parameter for violations, attack signatures, or meta-characters +- Decide whether to exclude certain violations, attack signatures, or meta-characters for a parameter + +The following example has two user-defined parameters. + +The first one, `text`, takes string values (here configured as alpha-numeric), and limits the length of the allowed string between 4 and 8 characters. Any string below or above these values will trigger the violation `VIOL_PARAMETER_VALUE_LENGTH`. Note that we enable this violation to *block* the violating request. + +The second parameter, `query`, is added to the policy just to avoid a false positive condition due to a specific signature, `200002835`. + +This allows you to create exceptions on known false positives _only_ within the context of a specific parameter. The signature will still be detected on values of other parameters. + +```json +{ + "policy": { + "name": "user_defined_parameters_data_types", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "text", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "alpha-numeric", + "checkMinValueLength": true, + "checkMaxValueLength": true, + "minimumLength": 4, + "maximumLength": 8 + }, + { + "name": "query", + "type": "explicit", + "valueType": "user-input", + "dataType": "alpha-numeric", + "signatureOverrides": [ + { + "enabled": false, + "signatureId": 200002835 + } + ] + } + ] + } +} +``` + +This next example uses a numeric parameter which accepts only integer values and allows values between 9 and 99 (non-inclusive). + +If the request includes anything other than an integer, it will trigger the `VIOL_PARAMETER_DATA_TYPE` violation. + +If the parameter value falls beyond or below the desired values, it will trigger the `VIOL_PARAMETER_NUMERIC_VALUE` violation. + +If you change the values of `exclusiveMin` and `exclusiveMax` to false, values equal to the boundary values will be accepted (namely 9 and 99). + +```json +{ + "policy": { + "name": "user_defined_parameters_data_types", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_NUMERIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_STATIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_DATA_TYPE", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "number", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "integer", + "checkMinValue": true, + "checkMaxValue": true, + "minimumValue": 9, + "maximumValue": 99, + "exclusiveMin": true, + "exclusiveMax": true + } + ] + } +} +``` + +For increased granularity, you can configure whether the parameter value is also a multiple of a specific number. + +This is useful when you wish to limit the input to specific values. + +The following example configures a parameter that accepts values in the range of 0 to 10 and are only multiples of 3. + +This means that the accepted values are 3, 6 and 9. Any other value will trigger the `VIOL_PARAMETER_NUMERIC_VALUE` violation. + +```json +{ + "policy": { + "name": "user_defined_parameters_data_types", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_NUMERIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_STATIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_DATA_TYPE", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "multiples", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "integer", + "checkMinValue": true, + "checkMaxValue": true, + "minimumValue": 0, + "maximumValue": 10, + "checkMultipleOfValue": true, + "multipleOf": 3 + } + ] + } +} +``` + +Another useful example is limiting the parameter to a single context, such as in a header or a query string. + +If the same variable appears in a different location, it will trigger the `VIOL_PARAMETER_LOCATION` violation. + +```json +{ + "policy": { + "name": "user_defined_parameters_misc_test", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_NUMERIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_STATIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_DATA_TYPE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_LOCATION", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "headerparam", + "type": "explicit", + "parameterLocation": "header", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "alpha-numeric", + "checkMinValueLength": false, + "checkMaxValueLength": false + } + ] + } +} +``` + +This final example configures: + +- A sensitive parameter `mypass` that should be masked in the logs +- A parameter `empty` that is allowed to be empty +- A parameter `repeated` that can be repeated multiple times +- A parameter `mandatory` that is mandatory for all requests + +New violations are enabled so that the configuration becomes effective. + +```json +{ + "policy": { + "name": "user_defined_parameters_misc_test", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_EMPTY_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_REPEATED", + "alarm": true, + "block": true + }, + { + "name": "VIOL_MANDATORY_PARAMETER", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "mypass", + "type": "explicit", + "parameterLocation": "any", + "sensitiveParameter": true, + "valueType": "auto-detect" + }, + { + "name": "empty", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": true, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "auto-detect" + }, + { + "name": "repeated", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": true, + "sensitiveParameter": false, + "valueType": "auto-detect" + }, + { + "name": "mandatory", + "type": "explicit", + "parameterLocation": "any", + "mandatory": true, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "auto-detect" + } + ] + } +} +``` \ No newline at end of file diff --git a/content/waf/policies/violations.md b/content/waf/policies/violations.md index 2317be48f..e1ab3fece 100644 --- a/content/waf/policies/violations.md +++ b/content/waf/policies/violations.md @@ -2,7 +2,7 @@ # We use sentence case and present imperative tone title: "Violations" # Weights are assigned in increments of 100: determines sorting order -weight: 150 +weight: 250 # Creates a table of contents and sidebar, useful for large documents toc: true # Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this