Saturday, May 09, 2026 AM03:21:24 HKT
@@ -0,0 +1,139 @@
|
||||
# **Naxsi Basic Configuration**
|
||||
|
||||
To get started with Naxsi, you can explore the following basic configuration.
|
||||
|
||||
## **Configuring Naxsi**
|
||||
|
||||
Naxsi must be configured based on what it is going to protect.
|
||||
|
||||
The first step, once compiled dynamically compiled, you will have a shared library which will need to be loaded by nginx by adding an entry to the `/etc/nginx/nginx.conf` file.
|
||||
|
||||
```nginx
|
||||
load_module /usr/lib/nginx/modules/ngx_http_naxsi_module.so;
|
||||
```
|
||||
|
||||
Once the module is added to the NGINX configuration, the next step is to **include global rules**; in the Naxsi repository you can find the [**`naxsi_core.rules`**](https://github.com/wargio/naxsi/blob/main/naxsi_rules/naxsi_core.rules) which gives to the user the ability to add the most basic ruleset to Naxsi itself.
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> It is possible to include these rules directly in `/etc/nginx/nginx.conf`.
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/naxsi/naxsi_core.rules
|
||||
```
|
||||
|
||||
The next step is configuring each website which will need to be protected by Naxsi; this happens by adding the directives `SecRulesEnabled`, `DeniedUrl` and `CheckRule` to a `location` block.
|
||||
|
||||
* `SecRulesEnabled`: is used to enable Naxsi in the `location` block.
|
||||
* `DeniedUrl`: specifies where blocked requests will be redirected (**this is an internal redirect for NGINX** and requires a different `location` block as destination).
|
||||
* `CheckRule`: takes an action (`LOG`, `BLOCK`, `DROP`, `ALLOW`) based on a specific score associated with the request.
|
||||
|
||||
For more details, check the [Directive chapter](directives.md)
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$FOO >= 8" BLOCK;
|
||||
}
|
||||
|
||||
# The location where all the blocked request will be internally redirected.
|
||||
location /RequestDenied {
|
||||
internal;
|
||||
return 403;
|
||||
}
|
||||
```
|
||||
|
||||
The last steps are create whitelists and configure the logging.
|
||||
|
||||
```nginx
|
||||
# example of whitelist (global and location-defined)
|
||||
MainRule wl:1000,1009,1315 "mz:$BODY_VAR:_wp_http_referer";
|
||||
BasicRule wl:1000,1009,1315 "mz:$BODY_VAR:_wp_http_referer";
|
||||
|
||||
# Enable JSON logs for Naxsi
|
||||
set $naxsi_json_log 1;
|
||||
```
|
||||
|
||||
## **Example Configuration**
|
||||
|
||||
This NGINX configuration for `/etc/nginx/nginx.conf` where we define a reverse proxy towards a webservice hosted on `internal-ip-address` on port `80`.
|
||||
|
||||
```nginx
|
||||
# load module
|
||||
load_module /etc/nginx/modules/ngx_http_naxsi_module.so;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com;
|
||||
|
||||
set $naxsi_json_log 1; # Enable JSON logs for Naxsi
|
||||
include /etc/nginx/naxsi/naxsi_core.rules; # Include core rules (see below)
|
||||
|
||||
location / {
|
||||
proxy_pass http://internal-ip-address:80;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
SecRulesEnabled; #enable naxsi for this `location`
|
||||
# LearningMode; #When enable, BLOCK CheckRule are considered as LOG.
|
||||
LibInjectionSql; #enable libinjection support for SQL injection detection
|
||||
LibInjectionXss; #enable libinjection support for XSS detection
|
||||
|
||||
# Internal denied request.
|
||||
DeniedUrl "/RequestDenied";
|
||||
|
||||
# Include additional rules
|
||||
include /etc/nginx/naxsi/blocking/*.rules;
|
||||
|
||||
# The following CheckRules are mandatory when using the rules found in the naxsi repository.
|
||||
# For more info, please check:
|
||||
# - https://github.com/wargio/naxsi/tree/main/naxsi_rules/blocking
|
||||
# - https://github.com/wargio/naxsi/blob/main/naxsi_rules/naxsi_core.rules
|
||||
|
||||
CheckRule "$SQL >= 8" BLOCK; # SQL injection action (unrelated to libinjection)
|
||||
CheckRule "$XSS >= 8" BLOCK; # XSS action (unrelated to libinjection)
|
||||
CheckRule "$RFI >= 8" BLOCK; # Remote File Inclusion action
|
||||
CheckRule "$UWA >= 8" BLOCK; # Unwanted Access action
|
||||
CheckRule "$EVADE >= 8" BLOCK; # Evade action (some tools may try to avoid detection).
|
||||
CheckRule "$UPLOAD >= 5" BLOCK; # Malicious upload action
|
||||
CheckRule "$TRAVERSAL >= 5" BLOCK; # Traversal access action
|
||||
CheckRule "$LIBINJECTION_XSS >= 8" BLOCK; # libinjection XSS action
|
||||
CheckRule "$LIBINJECTION_SQL >= 8" BLOCK; # libinjection SQLi action
|
||||
}
|
||||
|
||||
# The location where all the blocked request will be internally redirected.
|
||||
location /RequestDenied {
|
||||
internal;
|
||||
return 403;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration enables NAXSI and sets up basic rules for blocking requests based on various threat levels.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> The `SecRulesEnabled` directive is mandatory to enable NAXSI in a location.
|
||||
|
||||
Some key directives used in this example include:
|
||||
|
||||
* `LearningMode`: if enabled, `BLOCK` CheckRule will be considered as `LOG`, thus not blocking the requests.
|
||||
* `LibInjectionXss` and `LibInjectionSql`: When defined, enables libinjection support for SQLi & XSS and requires to define `$LIBINJECTION_XSS` & `$LIBINJECTION_SQL` **`**CheckRules**.
|
||||
* `include`: This directive allows to include other configuration files within the current scope, this can be useful if the system owner wants to have the same configuration for multiple websites without copy-pasting the same lines.
|
||||
|
||||
Additionally, this configuration includes directives for enabling libinjection's XSS and SQLi detection features.
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> **Be aware that Nginx will fail to load the configuration, if `ngx_http_naxsi_module.so` is not loaded**.
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> It is possible to test the NGINX configuration by using `nginx -t` from the command line.
|
||||
|
||||
# Go Back
|
||||
|
||||
[Table of Contents](index.md).
|
||||
@@ -0,0 +1,171 @@
|
||||
# **Installing Naxsi**
|
||||
|
||||
In this section you can find how to install and build naxsi on various distributions.
|
||||
|
||||
## **Ubuntu/Debian**
|
||||
|
||||
Ubuntu & Debian do not provide a package for this, but you can easily compile naxsi using `apt-get source` to fetch the correct version of nginx as follows.
|
||||
|
||||
1. **Download the required software**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Some Debian and Ubuntu distributions uses **`libpcre2-dev`** instad of `libpcre3-dev`.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Debian bookworm requires also **`libperl-dev`**
|
||||
|
||||
```bash
|
||||
# Install required software
|
||||
apt-get install build-essential ca-certificates \
|
||||
dpkg-dev zlib1g-dev libgd-dev libgeoip-dev \
|
||||
libpcre3-dev libperl-dev libssl-dev libxslt1-dev \
|
||||
gzip git nginx tar wget
|
||||
```
|
||||
|
||||
We also need to download **Naxsi**
|
||||
|
||||
```bash
|
||||
NAXSI_VERSION=X.Y
|
||||
wget "https://github.com/wargio/naxsi/releases/download/$NAXSI_VERSION/naxsi-$NAXSI_VERSION-src-with-deps.tar.gz"
|
||||
mkdir -p naxsi
|
||||
tar -C naxsi -xzf naxsi-$NAXSI_VERSION-src-with-deps.tar.gz
|
||||
```
|
||||
|
||||
And fetch the NGINX source via `apt-get source`.
|
||||
|
||||
```bash
|
||||
apt-get source nginx
|
||||
```
|
||||
|
||||
2. **Retrieve the distro compile flags**
|
||||
|
||||
To correctly build Naxsi for Debian/Ubuntu, you will need to retrieve the configure arguments (also called `compile flags`) using `nginx -V`, as shown below.
|
||||
|
||||
```bash
|
||||
nginx -V
|
||||
```
|
||||
|
||||
Example of output:
|
||||
|
||||
```
|
||||
nginx version: nginx/1.18.0 (Ubuntu)
|
||||
built with OpenSSL 1.1.1f 31 Mar 2020
|
||||
TLS SNI support enabled
|
||||
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-lUTckl/nginx-1.18.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-compat --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_gzip_static_module --without-http_browser_module --without-http_geo_module --without-http_limit_req_module --without-http_limit_conn_module --without-http_memcached_module --without-http_referer_module --without-http_split_clients_module --without-http_userid_module --add-dynamic-module=/build/nginx-lUTckl/nginx-1.18.0/debian/modules/http-echo
|
||||
```
|
||||
|
||||
To simplify this process, you can use the following command, which takes the output of `nginx -V` and modifies it; this can be used as a quick way to get "ready-to-use" configure arguments for building NGINX.
|
||||
|
||||
```bash
|
||||
nginx -V 2>&1 | grep "configure arguments:" | cut -d ":" -f2- | sed -e "s#/build/nginx-[A-Za-z0-9]*/#./#g" | sed 's/--add-dynamic-module=[A-Za-z0-9\/\._-]*//g'
|
||||
```
|
||||
|
||||
Example of output:
|
||||
|
||||
```
|
||||
--with-cc-opt='-g -O2 -fdebug-prefix-map=./nginx-1.18.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-compat --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_gzip_static_module --without-http_browser_module --without-http_geo_module --without-http_limit_req_module --without-http_limit_conn_module --without-http_memcached_module --without-http_referer_module --without-http_split_clients_module --without-http_userid_module
|
||||
```
|
||||
|
||||
3. **Build NGINX with Naxsi as module**
|
||||
|
||||
Now we will build Naxsi using NGINX sources:
|
||||
|
||||
```bash
|
||||
# Build NGINX with Naxsi
|
||||
cd nginx-*
|
||||
NGINX_BUILD_FLAGS=$(nginx -V 2>&1 | grep "configure arguments:" | cut -d ":" -f2- | sed -e "s#/build/nginx-[A-Za-z0-9]*/#./#g" | sed 's/--add-dynamic-module=[A-Za-z0-9\/\._-]*//g')
|
||||
./configure $NGINX_BUILD_FLAGS --add-dynamic-module=../naxsi/naxsi_src/
|
||||
make modules
|
||||
```
|
||||
|
||||
You will find the built module at the following path:
|
||||
|
||||
```
|
||||
nginx-<version>/objs/ngx_http_naxsi_module.so
|
||||
```
|
||||
|
||||
The other files you will need, are **the rules**, which can be found at the following path:
|
||||
|
||||
```
|
||||
naxsi/naxsi_rules
|
||||
```
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> **Be aware that you may encounter the following error related to `libinjection`, which can be safely ignored.**
|
||||
|
||||
```
|
||||
[truncated output ...]
|
||||
configuring additional dynamic modules
|
||||
adding module in ../naxsi/naxsi_src
|
||||
Package libinjection was not found in the pkg-config search path.
|
||||
Perhaps you should add the directory containing `libinjection.pc'
|
||||
to the PKG_CONFIG_PATH environment variable
|
||||
No package 'libinjection' found
|
||||
Package libinjection was not found in the pkg-config search path.
|
||||
Perhaps you should add the directory containing `libinjection.pc'
|
||||
to the PKG_CONFIG_PATH environment variable
|
||||
No package 'libinjection' found
|
||||
Using submodule libinjection
|
||||
+ naxsi was configured
|
||||
```
|
||||
|
||||
# **Compiling Naxsi from Sources**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> You will need to have a working C dev environment installed on your system, for tools like `gcc` or `clang` and `make`, in order to compile Naxsi.
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> You will need to have `libpcre` or `libpcre2` or `libpcre3` installed to correctly build Naxsi.
|
||||
|
||||
To compile Naxsi from source code, follow these steps:
|
||||
|
||||
1. **Get Naxsi sources**
|
||||
|
||||
```bash
|
||||
NAXSI_VERSION=X.Y
|
||||
wget "https://github.com/wargio/naxsi/releases/download/$NAXSI_VERSION/naxsi-$NAXSI_VERSION-src-with-deps.tar.gz"
|
||||
mkdir -p naxsi
|
||||
tar -C naxsi -xzf naxsi-$NAXSI_VERSION-src-with-deps.tar.gz
|
||||
```
|
||||
|
||||
2. **Get NGINX sources**
|
||||
|
||||
```bash
|
||||
NGINX_VERSION=X.Y.Z
|
||||
wget https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz
|
||||
mkdir -p nginx
|
||||
tar -C nginx -xzf nginx-$NGINX_VERSION.tar.gz
|
||||
```
|
||||
|
||||
3. **Build NGINX and Naxsi**
|
||||
|
||||
```
|
||||
cd nginx
|
||||
./configure --add-dynamic-module=../naxsi/naxsi_src/
|
||||
make modules
|
||||
```
|
||||
|
||||
4. **Install Nginx and Naxsi**
|
||||
|
||||
You can automatically install the files using `make install` or alternatively you can manually install the built module using:
|
||||
|
||||
You will find the built module at the following path:
|
||||
|
||||
```
|
||||
nginx/objs/ngx_http_naxsi_module.so
|
||||
```
|
||||
|
||||
The other files you will need, are **the rules**, which can be found at the following path:
|
||||
|
||||
```
|
||||
naxsi/naxsi_rules
|
||||
```
|
||||
|
||||
# Next
|
||||
|
||||
[Basic Configuration](basic-configuration.md).
|
||||
@@ -0,0 +1,252 @@
|
||||
# **Naxsi Directives**
|
||||
|
||||
This section explains all the directives, with examples, that are available when the Naxsi module (`ngx_http_naxsi_module.so`) is enabled.
|
||||
|
||||
## **SecRulesEnabled**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
This directive is mandatory to `enable` naxsi in a NGINX `location`.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
}
|
||||
```
|
||||
|
||||
## **CheckRule**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
This directive is required to instruct Naxsi on which action to take when there is a rule match.
|
||||
|
||||
The directive requires you to specify a **score** with a variable name and its min/max value (for example `$FOO_BAR >= 4`); the score is then followed by an action to take (`LOG`, `BLOCK`, `DROP`, or `ALLOW`) when is met.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> Score variable names must starts with a "dollar sign" `$` and can contains "underscores" `_`.
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> The action `BLOCK` will behave like `DROP` only when `LearningMode` is not enabled.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
CheckRule "$FOO_UU >= 8" LOG;
|
||||
CheckRule "$BARRRR < 99" DROP;
|
||||
CheckRule "$SOMETHING <= 33" BLOCK;
|
||||
}
|
||||
```
|
||||
|
||||
## **LibInjectionXss**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
When defined, this directive enables [libinjection's](https://github.com/libinjection/libinjection) xss detection on *all* requests.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> The detected XSS will increase the score `$LIBINJECTION_XSS` by `1` for each match; this means that is required to define `$LIBINJECTION_XSS` as a `CheckRule`.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
# enable libinjection xss
|
||||
LibInjectionXss;
|
||||
# define LIBINJECTION_XSS for libinjection
|
||||
CheckRule "$LIBINJECTION_XSS >= 8" BLOCK;
|
||||
}
|
||||
```
|
||||
|
||||
## **LibInjectionSql**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
When defined, this directive enables [libinjection's](https://github.com/libinjection/libinjection) sqli detection on *all* requests.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> The detected SQLi will increase the score `$LIBINJECTION_SQL` by `1` for each match; this means that is required to define `$LIBINJECTION_SQL` as a `CheckRule`.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
# enable libinjection sqli
|
||||
LibInjectionSql;
|
||||
# define LIBINJECTION_SQL for libinjection
|
||||
CheckRule "$LIBINJECTION_SQL >= 8" BLOCK;
|
||||
}
|
||||
```
|
||||
|
||||
## **LearningMode**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
This directive instructs Naxsi that to not honor `CheckRules` which defines actions as `BLOCK` in a NGINX `location`.
|
||||
|
||||
All the `BLOCK` actions will be interpreted as `LOG`; this is a useful mode when deploying a new web application and detect all false positives that might be generated by the WAF.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> Keep in mind that internal rules (those with an `id` inferior to 1000) will drop the request even in learning mode, because it means something fishy is going on and Naxsi can't correctly process the request. You can of course apply whitelists if those are false positives.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
# enable Naxsi learning mode
|
||||
LearningMode;
|
||||
}
|
||||
```
|
||||
|
||||
## **DeniedUrl**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
This directive is used to define where Naxsi has to redirect (it's an NGINX's internal redirect) when blocking, dropping or logging requests.
|
||||
|
||||
The following headers that are added are when blocking, dropping or logging requests:
|
||||
- `x-orig_url`
|
||||
- `x-orig_args`
|
||||
- `x-naxsi_sig`
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> It is **strongly** suggested to mark the `DeniedUrl` location as `internal` to prevent possible pre-detection of the WAF as per example.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
DeniedUrl "/RequestDenied";
|
||||
}
|
||||
|
||||
location /RequestDenied {
|
||||
# Mark this location as internal only to prevent possible pre-detection of the WAF
|
||||
internal;
|
||||
# return code of the location.
|
||||
return 403;
|
||||
}
|
||||
```
|
||||
|
||||
## **MainRule**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `http`
|
||||
|
||||
This directive is required to declare a **global** [rule](rules.md) or a [whitelist](whitelist.md).
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> You can define these within a config file and use the `include` directive to include them within the NGINX configuration.
|
||||
|
||||
You can find within the [Naxsi source code a list of global rules](https://github.com/wargio/naxsi/blob/main/naxsi_rules/) which provides a basic ruleset to protect any web application; these rules requires to include the following `CheckRules`:
|
||||
|
||||
```nginx
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 5" BLOCK;
|
||||
CheckRule "$UPLOAD >= 5" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
CheckRule "$UWA >= 8" BLOCK;
|
||||
CheckRule "$EVADE >= 8" BLOCK;
|
||||
```
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
http {
|
||||
# global whitelist
|
||||
MainRule wl:12345 "mz:$URL:/robots.txt|URL";
|
||||
# global rule
|
||||
MainRule id:45678 "s:$UWA:8" "str:nmap" "mz:$HEADERS_VAR:User-Agent" "msg:nmap in user-agent";
|
||||
}
|
||||
```
|
||||
|
||||
## **BasicRule**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
This directive is required to declare a **location-specific** (i.e. not global) [rule](rules.md) or a [whitelist](whitelist.md).
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> You can define these within a config file and use the `include` directive to include them within the NGINX configuration.
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> You can find within the [Naxsi source code a list of location-specific whitelist](https://github.com/wargio/naxsi/tree/main/naxsi_rules/whitelists) which can be used for known web applications like Wordpress, Etherpad, Drupal, and more...
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
# location-specific whitelist
|
||||
BasicRule wl:12345 "mz:$URL:/robots.txt|URL";
|
||||
# location-specific rule
|
||||
BasicRule id:45678 "s:$UWA:8" "str:nmap" "mz:$HEADERS_VAR:User-Agent" "msg:nmap in user-agent";
|
||||
}
|
||||
```
|
||||
|
||||
## **IgnoreIP**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
This directive can be used to whitelist requests from certain IPs.
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> You can define these within a config file and use the `include` directive to include them within the NGINX configuration.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
IgnoreIP "1.2.3.4";
|
||||
IgnoreIP "2001:4860:4860::8844";
|
||||
}
|
||||
```
|
||||
|
||||
## **IgnoreCIDR**
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> NGINX block: `location`
|
||||
|
||||
This directive can be used to whitelist requests from certain IP ranges.
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> You can define these within a config file and use the `include` directive to include them within the NGINX configuration.
|
||||
|
||||
### Example:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
IgnoreCIDR "192.168.0.0/24";
|
||||
IgnoreCIDR "2001:4860:4860::/112";
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,79 @@
|
||||
# **Naxsi Web Application Firewall Documentation**
|
||||
|
||||
Naxsi is a Free Open Source Web Application Firewall which runs under NGINX.
|
||||
|
||||
## What is a Web Application Firewall (WAF)?
|
||||
|
||||
A Web Application Firewall (WAF) is a security system that sits in front of a web application to inspect, filter, and block malicious traffic. It acts as an intermediary between the internet and your website or web application, examining HTTP requests and responses for potential threats.
|
||||
|
||||
One of the key features of WAFs is virtual patching, which allows them to protect against known vulnerabilities without requiring actual patches to be applied to the underlying code. Here's how it works on Naxsi:
|
||||
|
||||
1. **Rule-based detection**: The WAF has a list of known patterns, i.e. malicious request, which are used to block requests.
|
||||
2. **Request analysis**: When an incoming HTTP request is received, the WAF analyzes its contents against the list of patterns.
|
||||
3. **Threat identification**: If the request matches a known pattern, the WAF first checks if there is a whitelist rule and if not, then it assigns a score to the request; if this score exceeds the score limits then the request is blocked.
|
||||
|
||||
Naxsi is also capable to employ a whitelisting strategy, where all incoming traffic is initially blocked by default. In this case, only the requests that match specific rules are then allowed to pass through and reach the web application behind the WAF. This approach provides an added layer of security by assuming all unknown traffic poses a potential threat until proven otherwise.
|
||||
|
||||
## Virtual patching
|
||||
|
||||
Sometimes it happens that the web application behind the WAF might have a security vulnerability that cannot be patched immediately by the system owner; Naxsi can apply "virtual patches" to block the vulnerability without requiring changes to the underlying web application code.
|
||||
|
||||
By using virtual patching, the system owner can:
|
||||
|
||||
* Protect against zero-day attacks and unknown vulnerabilities
|
||||
* Prevent exploitation of unpatched or outdated software versions
|
||||
* Reduce the attack surface by blocking suspicious requests before they reach your website
|
||||
|
||||
These virtual patches are expressed in form of Naxsi rules and can be applied to RAW requests or specific fields within the request.
|
||||
|
||||
## What does it run on?
|
||||
|
||||
Naxsi should be compatible with any NGINX version, and is reported to work great on NetBSD, FreeBSD, OpenBSD, Debian, Ubuntu and CentOS based systems.
|
||||
|
||||
## Why is it different?
|
||||
|
||||
Contrary to most Web Application Firewalls, Naxsi doesn't rely on a signature base like an antivirus, and thus cannot be circumvented by an "unknown" attack pattern.
|
||||
|
||||
# Getting Started
|
||||
|
||||
* **Installing Naxsi**: [Learn how to compile and install Naxsi from source code.](build-naxsi.md)
|
||||
* **Basic Configuration**: [A basic configuration for Naxsi](basic-configuration.md).
|
||||
|
||||
# Configuration Options
|
||||
|
||||
* **Directives**: [Explains all the directives that are available when the Naxsi module is enabled.](directives.md)
|
||||
* **Rules**: [Understand the different types of rules you can create in Naxsi.](rules.md)
|
||||
* **Internal Rules**: [The full list of internal rules that are hardcoded in Naxsi](internal_rules.md)
|
||||
* **Whitelists**: [Whitelisting to resolve false positives in Naxsi.](whitelist.md)
|
||||
* **Matchzones**: [How zones can be used to filter rules or whitelists.](matchzones.md)
|
||||
* **Logs**: [Log format and their content.](logs.md)
|
||||
* **Packaging Naxsi**: [Build your own distro packages from sources.](packaging-naxsi.md)
|
||||
|
||||
# Support & Bugs
|
||||
|
||||
Questions & bug reports regarding NAXSI can be addressed via issues.
|
||||
|
||||
[Click here to open an issue](https://github.com/wargio/naxsi/issues/new)
|
||||
|
||||
# Vulnerability disclosure
|
||||
|
||||
When disclosing vulnerabilities, please send first an email to `deroad at kumo.xn--q9jyb4c` (gpg keyid: `29656E856786B9A1FBF983CFA219F52A8217B1FE`)
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
mDMEXR3FZhYJKwYBBAHaRw8BAQdAfuSDE68TEppjJfUAApXSTsHrKtGefVJXvz7f
|
||||
YIO0gci0MUdpb3Zhbm5pIERhbnRlIEdyYXppb2xpIDxkZXJvYWRAa3Vtby54bi0t
|
||||
cTlqeWI0Yz6IkAQTFggAOAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBCll
|
||||
boVnhrmh+/mDz6IZ9SqCF7H+BQJg9+CGAAoJEKIZ9SqCF7H+X04BAPXz7R856z72
|
||||
f8nsZZjj4q3YaJbXA3pSVLIJ9uDQniCsAP9PaPBcbr231M3cjjBMo7ovlrElfFor
|
||||
zCWA3NhRb4Y2DLg4BF0dxWYSCisGAQQBl1UBBQEBB0AVby+EIQcIoqSDZelvkqt8
|
||||
dV1kvF4f/J0jj0k3OEKNcAMBCAeIeAQYFggAIAIbDBYhBCllboVnhrmh+/mDz6IZ
|
||||
9SqCF7H+BQJg9+C4AAoJEKIZ9SqCF7H+ZfQBAOFb7iZm7t8j5ymiXyJFcuM/nF9+
|
||||
bx4+KJJUTR9r6zBFAQD3Ea5Ya4lny/v9WKNSpBfZFEs3pkDnfgxw3o8vc4iSAQ==
|
||||
=d/IR
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
# **Deprecated Documentation**
|
||||
|
||||
The deprecated/old documentation is accessible [here](old/index.md).
|
||||
@@ -0,0 +1,178 @@
|
||||
# **Internal Rules**
|
||||
|
||||
Naxsi has some internal rules that are hardcoded within the WAF; these rules are defined by **ids** lower than **1000**.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> The internal blocking rules can be whitelisted.
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> No rules shall be defined with **ids** lower than 1000.
|
||||
|
||||
## Internal Rule 1 - Weird Request
|
||||
|
||||
> ❌ **Deprecated**
|
||||
>
|
||||
> Number: **1**
|
||||
> Name: **Weird Request**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `1` refers to any request that contains weird request which failed to be parsed by Naxsi.
|
||||
|
||||
## Internal Rule 2 - Big Request
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **2**
|
||||
> Name: **Big Request**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `2` refers to any request that is too big to be parsed; this only happens when NGINX has to create a temporary file on the filesystem or when the content-size mismatch with the actual body size.
|
||||
|
||||
## Internal Rule 10 - Hex Encoded Null-Bytes
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **10**
|
||||
> Name: **Null-Byte Hex Encoding**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `10` refers to any request that contains one or many hex encoded null-bytes (i.e. `0x00` or `\x00`).
|
||||
|
||||
## Internal Rule 11 - Uncommon Content Type
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **11**
|
||||
> Name: **Uncommon Content Type**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `11` refers to any request that contains uncommon content type; this happens when `Content-Type` header is missing or during a POST request, the `Content-Type` is not one of the followings:
|
||||
|
||||
- `"application/x-www-form-urlencoded"`
|
||||
- `"multipart/form-data"`
|
||||
- `"application/json"`
|
||||
- `"application/vnd.api+json"`
|
||||
- `"application/csp-report"`
|
||||
|
||||
## Internal Rule 12 - Invalid formatted URL
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **12**
|
||||
> Name: **Invalid formatted URL**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `12` refers to any request that contains a badly formatted URL; this happens when the HTTP request has an invalid URL (this may be caught before-hand by NGINX which may return 400).
|
||||
|
||||
## Internal Rule 13 - Malformed POST Format
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **13**
|
||||
> Name: **Malformed POST Format**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `13` refers to any request that contains a malformed POST, for example missing `content-disposition`, malformed boundary line, missing name, missing `Content-Type`, etc...
|
||||
|
||||
## Internal Rule 14 - Malformed POST Boundary
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **14**
|
||||
> Name: **Malformed POST Boundary**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `14` refers to any request that contains a malformed POST boundary.
|
||||
|
||||
## Internal Rule 15 - Malformed JSON
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **15**
|
||||
> Name: **Malformed JSON**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `15` refers to any request that contains malformed JSON.
|
||||
|
||||
## Internal Rule 16 - Empty POST Body
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **16**
|
||||
> Name: **Empty POST Body**
|
||||
> Action: **BLOCK**
|
||||
|
||||
The internal rule `16` refers to any request that contains empty POST body.
|
||||
|
||||
## Internal Rule 17 - libinjection SQLi
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **17**
|
||||
> Name: **libinjection SQLi**
|
||||
> Score: **$LIBINJECTION_SQL**
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> This rule does not block a request, but increases the score `$LIBINJECTION_SQL` by **1**.
|
||||
|
||||
The internal rule `17` refers to any request that contains sql injections detected by libinjection.
|
||||
|
||||
See also [Directive `LibInjectionSql`](directives.md#libinjectionsql) for more details.
|
||||
|
||||
## Internal Rule 18 - libinjection XSS
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **18**
|
||||
> Name: **libinjection Xss**
|
||||
> Score: **$LIBINJECTION_XSS**
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> This rule does not block a request, but increases the score `$LIBINJECTION_XSS` by **1**.
|
||||
|
||||
The internal rule `18` refers to any request that contains XSS injections detected by libinjection.
|
||||
|
||||
See also [Directive `LibInjectionXss`](directives.md#libinjectionxss) for more details.
|
||||
|
||||
## Internal Rule 19 - No Rules Loaded
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **19**
|
||||
> Name: **No Rules**
|
||||
> Action: **DROP**
|
||||
|
||||
The internal rule `19` is triggered only when the WAF is enabled but no global and no location-specific rules has been loaded at the current location.
|
||||
|
||||
## Internal Rule 20 - Malformed UTF-8
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **20**
|
||||
> Name: **Malformed UTF-8**
|
||||
> Action: **DROP**
|
||||
|
||||
The internal rule `20` refers to any request that contains malformed UTF-8.
|
||||
|
||||
## Internal Rule 21 - Illegal Host in Header
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Number: **21**
|
||||
> Name: **Illegal Host in Header**
|
||||
> Action: **DROP**
|
||||
|
||||
The internal rule `21` refers to any request that contains a host header with an illegal ip:
|
||||
|
||||
- `0.0.0.0/8`
|
||||
- `255.255.255.255/32`
|
||||
- `0000:0000:0000:0000:0000:0000:0000:0000/128`
|
||||
- `ff00:0000:0000:0000:0000:0000:0000:0000/8`
|
||||
|
||||
# Go Back
|
||||
|
||||
[Rules](rules.md).
|
||||
@@ -0,0 +1,116 @@
|
||||
# **Naxsi Logs**
|
||||
|
||||
Naxsi utilizes NGINX's `error_log` feature to produce logs; specifically, it offers two types of error logs that can be generated: one in **URL-encoded format** (which is the default option) and another in **JSON format**.
|
||||
|
||||
These logs are `action log` and `extended log`. They include detailed information about the triggered rules, content identified, paths and request IDs for comprehensive security monitoring and analysis.
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> Due to hardcoded limitations of NGINX (see `NGX_MAX_ERROR_STR` defined by `ngx_log.h` in the NGINX source code), these logs may be truncated if too long.
|
||||
>
|
||||
> It is strongly recommanded to increase the line limit to `8192` or higher by patching NGINX itself.
|
||||
>
|
||||
> Example of patch: `sed -i 's#NGX_MAX_ERROR_STR 2048#NGX_MAX_ERROR_STR 8192#g' src/core/ngx_log.h`
|
||||
|
||||
## Action Logs
|
||||
|
||||
Action logs contains the following information:
|
||||
|
||||
- `ip`: Client IP
|
||||
- `server`: Nginx server name
|
||||
- `uri`: Request URI
|
||||
- `config`: Configuration of naxsi for the applied rule, this can be one of the following values:
|
||||
- `learning` [Learning Mode active](directives.md#learningmode), i.e. The [action taken is `LOG`](directives.md#checkrule) and the **request was NOT dropped**.
|
||||
- `learning-drop` [Learning Mode active](directives.md#learningmode) but request was dropped, i.e. [The action taken is `DROP`](directives.md#checkrule).
|
||||
- `drop` The request was dropped, i.e. [The action taken is `DROP`](directives.md#checkrule).
|
||||
- `block` The request was dropped, i.e. [The action taken is `BLOCK`](directives.md#checkrule).
|
||||
- `ignore` The request was supposed to be dropped/blocked, but it was ignored due to matching [`IgnoreIP`](directives.md#ignoreip) or [`IgnoreCIDR`](directives.md#ignorecidr), i.e. [The action taken is `LOG`](directives.md#checkrule).
|
||||
- `rid`: Request Identifier (matches the `request_id` NGINX value).
|
||||
- `id<N>`: Matched rule identifier
|
||||
- `cscore<N>`: Request score name (see [Check Rules](directives.md#checkrule)), for example `$SQL`.
|
||||
- `score<N>`: Request score value (see [Check Rules](directives.md#checkrule)), for example `8`.
|
||||
- `zone<N>`: Request [matchzone](matchzones.md), for example `URL`.
|
||||
- `var_name<N>`: Request variable name where the match has happen, for example `foo_bar[]`.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> The `<N>` is a counter that always starts from `0` for each request and is used for listing all the matched rules and their info (`id`, `cscore`, etc..).
|
||||
>
|
||||
> For example, a request can have multiple matching rules and these will be logged by Naxsi as follows:
|
||||
>
|
||||
> The first rule (`<N>=0`) is logged as `id0=<id>`, `cscore0=<score name>`, `score0=<score count>`, `zone0=<matchzone>`, `var_name0=<var name>`; the second rule (`<N>=1`) is going to be `id1=<id>`, `cscore1=<score name>`, `score1=<score count>`, `zone1=<matchzone>`, `var_name1=<var name>`, the third (`<N>=2`), etc...
|
||||
>
|
||||
> If the rule score name is the same for multiple rules, the first `cscoreX` defined in the logs will contain the sum of the total score of multiple rules.
|
||||
|
||||
## Extended Logs
|
||||
|
||||
Extended logs needs to be enabled by adding `set $naxsi_extensive_log 1;` or via the runtime modifier `naxsi_extensive_log` to the NGINX configuration and logs all the matches of a request:
|
||||
|
||||
- `ip`: Client IP
|
||||
- `server`: Nginx server name
|
||||
- `rid`: Request Identifier (matches the `request_id` NGINX value).
|
||||
- `uri`: Request URI
|
||||
- `id`: Matched rule identifier
|
||||
- `zone`: Request [matchzone](matchzones.md), for example `URL`.
|
||||
- `var_name`: Request variable name where the match has happen, for example `foo_bar[]`.
|
||||
- `content`: The matched content, for example `malicious` (can be truncated if too long).
|
||||
|
||||
|
||||
*Action format logs* can be distinguished by *extended logs* the presence of `cscore` & `score` keywords; *Extended logs* are also the only ones having the `content` keyword.
|
||||
|
||||
## URL Encoded logs
|
||||
|
||||
**This is the default logging format for Naxsi**; These logs are always preceded by `NAXSI_FMT` (action logs) or `NAXSI_EXLOG` (extended logs) and the values of each logged info is `url-encoded`.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
2024/12/26 12:27:44 [error] 3829059#0: *4 NAXSI_EXLOG: ip=127.0.0.1&server=localhost&rid=70d8cd8818e7e27a11d14df63c676227&uri=%2Fx%2Cy&id=1015&zone=URL&var_name=&content=%2Fx%2Cy, client: 127.0.0.1, server: localhost, request: "GET /x,y?uuu=b,c HTTP/1.1", host: "localhost"
|
||||
2024/12/26 12:27:44 [error] 3829059#0: *4 NAXSI_EXLOG: ip=127.0.0.1&server=localhost&rid=70d8cd8818e7e27a11d14df63c676227&uri=%2Fx%2Cy&id=1015&zone=ARGS&var_name=uuu&content=b%2Cc, client: 127.0.0.1, server: localhost, request: "GET /x,y?uuu=b,c HTTP/1.1", host: "localhost"
|
||||
2024/12/26 12:27:44 [error] 3829059#0: *4 NAXSI_FMT: ip=127.0.0.1&server=localhost&uri=%2Fx%2Cy&config=learning&rid=70d8cd8818e7e27a11d14df63c676227&cscore0=$SQL&score0=8&zone0=URL&id0=1015&var_name0=&zone1=ARGS&id1=1015&var_name1=uuu, client: 127.0.0.1, server: localhost, request: "GET /x,y?uuu=b,c HTTP/1.1", host: "localhost"
|
||||
```
|
||||
|
||||
## JSON logs
|
||||
|
||||
This logs format needs to be enabled via `set $naxsi_json_log 1;` or via the runtime modifier `naxsi_json_log`.
|
||||
|
||||
*Action logs* can be distinguished by *extended logs* the presence of `cscore` & `score` keywords; *Extended logs* are also the only ones having the `content` keyword.
|
||||
|
||||
```
|
||||
2024/12/26 12:28:37 [error] 3829158#0: *4 {"ip":"127.0.0.1","server":"localhost","rid":"1d27ac3c0b10bbb8783d109213f3f4cd","uri":"/x,y","id":1015,"zone":"URL","var_name":"","content":"/x,y"}, client: 127.0.0.1, server: localhost, request: "GET /x,y?uuu=b,c HTTP/1.1", host: "localhost"
|
||||
2024/12/26 12:28:37 [error] 3829158#0: *4 {"ip":"127.0.0.1","server":"localhost","rid":"1d27ac3c0b10bbb8783d109213f3f4cd","uri":"/x,y","id":1015,"zone":"ARGS","var_name":"uuu","content":"b,c"}, client: 127.0.0.1, server: localhost, request: "GET /x,y?uuu=b,c HTTP/1.1", host: "localhost"
|
||||
2024/12/26 12:28:37 [error] 3829158#0: *4 {"ip":"127.0.0.1","server":"localhost","uri":"/x,y","config":"block","rid":"1d27ac3c0b10bbb8783d109213f3f4cd","cscore0":"$SQL","score0":8,"zone0":"URL","id0":1015,"var_name0":"","zone1":"ARGS","id1":1015,"var_name1":"uuu"}, client: 127.0.0.1, server: localhost, request: "GET /x,y?uuu=b,c HTTP/1.1", host: "localhost"
|
||||
```
|
||||
|
||||
Here is an example of how the previous JSON logs appears when formatted by utilities such as `jq`:
|
||||
|
||||
```json
|
||||
// Action Log
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"server": "localhost",
|
||||
"uri": "/x,y",
|
||||
"config": "block",
|
||||
"rid": "1d27ac3c0b10bbb8783d109213f3f4cd",
|
||||
"cscore0": "$SQL",
|
||||
"score0": 8,
|
||||
"zone0": "URL",
|
||||
"id0": 1015,
|
||||
"var_name0": "",
|
||||
"zone1": "ARGS",
|
||||
"id1": 1015,
|
||||
"var_name1": "uuu"
|
||||
}
|
||||
|
||||
// Extended Log
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"server": "localhost",
|
||||
"rid": "1d27ac3c0b10bbb8783d109213f3f4cd",
|
||||
"uri": "/x,y",
|
||||
"id": 1015,
|
||||
"zone": "URL",
|
||||
"var_name": "",
|
||||
"content": "/x,y"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,222 @@
|
||||
# **Naxsi Matchzones**
|
||||
|
||||
**Matchzones**, denoted by the prefix `mz:`, are crucial components of **rules** and **whitelists**. They act like filters to define specific locations where a pattern should be searched or allowed.
|
||||
|
||||
Here's how they function differently based on the context:
|
||||
|
||||
- **Rules:** In this case, matchzones work with an **OR** logic (like `BODY` OR `HEADERS`). This means that as long as one of the specified conditions is met, the rule triggers.
|
||||
|
||||
- **Whitelists:** Here, matchzones operate under an **AND** logic (like `url` must be `/foo` AND must occur in `ARGS`). It requires that *both conditions* within a whitelist be satisfied before the pattern is allowed.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> Naxsi decodes any `url-encoded` or `hexadecimal` sequence, this means the string or regex to search for must be of the decoded content (**this applies also to URLs**).
|
||||
>
|
||||
> Example: `1%20UnioN%20SeLEct%201` becomes `1 UnioN SeLEct 1` before applying rules.
|
||||
|
||||
## Any Matchzone
|
||||
|
||||
This special matchzone designated by `ANY` allows to define **rules and whitelists which matches in any area of a request**.
|
||||
|
||||
For instance, the rule `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:ANY";` is equivalent of writing the following rules but in one line.
|
||||
|
||||
```
|
||||
MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:ARGS|HEADERS|BODY|URL";
|
||||
MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:RAW_BODY";
|
||||
MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:FILE_EXT";
|
||||
```
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> This can be used also for whitelists, but it is possible to disable a rule by just not declaring any matchzone (see the [Whitelist matchzones notes](whitelist.md#matchzone)).
|
||||
|
||||
## Filter by HTTP Headers
|
||||
|
||||
HTTP headers let the client and the server pass additional information with a message in a request or response; Naxsi allows to filter rules and whitelists by headers as follows:
|
||||
|
||||
### Filter by Any HTTP header value
|
||||
|
||||
This Matchzone designated by `HEADERS` is specifically tailored to identify **only the content found within HTTP headers**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:HEADERS";` detects the occurrence of the string `malicious` exclusively within the HTTP headers values transmitted in a request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:HEADERS";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP header values.
|
||||
|
||||
### Filter by Any HTTP header name
|
||||
|
||||
This Matchzone designated by `HEADERS|NAME` is specifically tailored to identify **only the name of the header found within HTTP request**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8" "str:x-forward-to" "mz:HEADERS|NAME";` detects the occurrence of the string `X-Forward-To` exclusively within the HTTP headers names transmitted in a request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:HEADERS|NAME";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP header name.
|
||||
|
||||
### Filter by HTTP header name
|
||||
|
||||
This Matchzone designated by `$HEADERS_VAR:foo` and `$HEADERS_VAR_X:^foo$` is specifically tailored to identify **only the content found within an HTTP header named `foo`**.
|
||||
|
||||
- `$HEADERS_VAR:<string>` can be used to filter by header name (**case-insensitive**) via a string.
|
||||
- `$HEADERS_VAR_X:<regex>` can be used to filter by header name (**case-insensitive**) via a regex.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:curl" "mz:$HEADERS_VAR:user-agent|$HEADERS_VAR:cookie";` detects the occurrence of the string `curl` exclusively within the value of the HTTP headers `User-Agent` and `Cookie` (**case-insensitive**).
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:$HEADERS_VAR_X:^cookie$";` negates the match of any rule with id `12345` if the match occurs within the value of the HTTP header `Cookie` via regex.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> This can be mixed with `|NAME` to perform the filtering at argument name instead of value.
|
||||
> Example: `mz:$HEADERS_VAR_X:^foo\d+$|NAME` matches only the HTTP header named `foo<number>`.
|
||||
|
||||
## Filter by GET query
|
||||
|
||||
HTTP GET requests can carry information, referred as queries, in the form of key=value pairs; Naxsi allows to filter rules and whitelists by these arguments as follows:
|
||||
|
||||
### Filter by Any GET query value
|
||||
|
||||
This Matchzone designated by `ARGS` is specifically tailored to identify **only the value found within HTTP GET query**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:ARGS";` detects the occurrence of the string `malicious` exclusively within the HTTP GET queries values transmitted in a request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:ARGS";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP GET queries values.
|
||||
|
||||
### Filter by Any GET query name
|
||||
|
||||
This Matchzone designated by `ARGS|NAME` is specifically tailored to identify **only the name of the GET query found within HTTP request**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8" "str:delete_action" "mz:ARGS|NAME";` detects the occurrence of the string `delete_action` exclusively within the HTTP GET queries names transmitted in a request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:ARGS|NAME";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP GET queries names.
|
||||
|
||||
### Filter by GET query value or name
|
||||
|
||||
This Matchzone designated by `$ARGS_VAR:foo` and `$ARGS_VAR_X:^foo$` is specifically tailored to identify **only the content found within an HTTP GET query named `foo`**.
|
||||
|
||||
- `$ARGS_VAR:<string>` can be used to filter by argument name (**case-insensitive**) via a string.
|
||||
- `$ARGS_VAR_X:<regex>` can be used to filter by argument name (**case-insensitive**) via a regex.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:evil" "mz:$ARGS_VAR:foo|$ARGS_VAR:bar";` detects the occurrence of the string `evil` exclusively within the value of the GET queries `User-Agent` (**case-insensitive**).
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:$ARGS_VAR_X:^cookie$";` negates the match of any rule with id `12345` if the match occurs within the value of the HTTP header `Cookie` via regex.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> This can be mixed with `|NAME` to perform the filtering at argument name instead of value.
|
||||
> Example: `mz:$ARGS_VAR_X:^foo\d+$|NAME` matches only the GET query named `foo<number>`.
|
||||
|
||||
## Filter by POST Requests
|
||||
|
||||
HTTP POST requests carries information in the HTTP body; the request data can have multiple formats:
|
||||
|
||||
- `application/x-www-form-urlencoded` contains key=value pairs.
|
||||
- `multipart/form-data` contains boundaries with the raw data.
|
||||
|
||||
Naxsi allows to filter these in rules and whitelists as follows:
|
||||
|
||||
### Filter by Any `application/x-www-form-urlencoded` Value
|
||||
|
||||
This Matchzone designated by `BODY` is specifically tailored to identify **only the value found within HTTP POST body**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:BODY";` detects the occurrence of the string `malicious` exclusively within the HTTP POST body values (key=value format) transmitted in a request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:BODY";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP POST body values.
|
||||
|
||||
### Filter by Any `application/x-www-form-urlencoded` Key
|
||||
|
||||
This Matchzone designated by `BODY|NAME` is specifically tailored to identify **only the name of the header found within HTTP request**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8" "str:delete_action" "mz:BODY|NAME";` detects the occurrence of the string `delete_action` exclusively within the HTTP POST arguments names transmitted in a request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:BODY|NAME";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP POST arguments names.
|
||||
|
||||
### Filter by Any `application/x-www-form-urlencoded` Key and Value
|
||||
|
||||
This Matchzone designated by `$BODY_VAR:foo` and `$BODY_VAR_X:^foo$` is specifically tailored to identify **only the content found within an HTTP POST body named `foo`**.
|
||||
|
||||
- `$BODY_VAR:<string>` can be used to filter by POST form name (**case-insensitive**) via a string.
|
||||
- `$BODY_VAR_X:<regex>` can be used to filter by POST form name (**case-insensitive**) via a regex.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:$BODY_VAR:foo|$BODY_VAR:bar";` detects the occurrence of the string `malicious` exclusively within the value of the POST form keys `foo` and `body` (**case-insensitive**).
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:$BODY_VAR_X:^foo$";` negates the match of any rule with id `12345` if the match occurs within the value of the POST form named `foo` via regex.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> This can be mixed with `|NAME` to perform the filtering at argument name instead of value.
|
||||
> Example: `mz:$BODY_VAR_X:^foo\d+$|NAME` matches only the POST argument named `foo<number>`.
|
||||
|
||||
### Filter by Any `multipart/form-data` filename
|
||||
|
||||
This Matchzone designated by `FILE_EXT` is specifically tailored to match **only the filename found within HTTP multipart POST request**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:.php" "mz:FILE_EXT";` detects the occurrence of the string `.php` exclusively within the filename of the HTTP multipart POST request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:FILE_EXT";` negates the match of any rule with id `12345` if the match itself occurs in the filename of the HTTP multipart POST request.
|
||||
|
||||
## Filter by HTTP Raw Body
|
||||
|
||||
This Matchzone designated by `RAW_BODY` is specifically tailored to match **any byte sequence in an unparsed HTTP body**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `BasicRule id:12345 "s:$EXECUTABLE:8" "rx:MZ\x90" "mz:RAW_BODY";` detects the occurrence of a byte sequence (Windows PE magic) within the HTTP body of the request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:RAW_BODY";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP body of the request.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> This matchzone is internally converted as `BODY` zone, thus the `BasicRule wl:12345 "mz:BODY";` and `BasicRule wl:12345 "mz:RAW_BODY";` are equivalent.
|
||||
|
||||
## Filter by HTTP URL
|
||||
|
||||
Naxsi supports filtering by HTTP URL as follows:
|
||||
|
||||
### Filter by HTTP URL (global)
|
||||
|
||||
This Matchzone designated by `URL` is specifically tailored to identify **only the value found within HTTP URL**.
|
||||
|
||||
For instance:
|
||||
|
||||
- A rule such as `MainRule id:12345 "s:$FOO:8,$BAR:4" "str:/admin" "mz:URL";` detects the occurrence of the string `/admin` exclusively within the HTTP URL transmitted in a request.
|
||||
- A whitelist entry like `BasicRule wl:12345 "mz:URL";` negates the match of any rule with id `12345` if the match itself occurs in the HTTP URL.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> This matchzone is globally applied, it is possible to restrict the matchzone to a specific URL or substring in the URL via `$URL` or `$URL_X` (see below).
|
||||
|
||||
### Filter by HTTP URL (restricted)
|
||||
|
||||
This Matchzone designated by `$URL:/foo` and `$URL_X:^/foo$` is specifically tailored to identify **only the URL containing the string `/foo`**.
|
||||
|
||||
- `$URL:<string>` can be used to filter by string (**case-insensitive**).
|
||||
- `$URL_X:<regex>` can be used to filter by regex (**case-insensitive**).
|
||||
|
||||
These can be mixed with all the previous matchzones as follows:
|
||||
|
||||
In rules context, `$URL` or `$URL_X` *must* be satisfied if present. Any other condition is treated as *OR* (opposite to whitelists).
|
||||
|
||||
- The rule `BasicRule str:Y id:X "mz:ARGS|BODY";` is interpreted as _pattern 'Y' will be matched against *any* GET and POST arguements_
|
||||
- The rule `BasicRule str:Y id:X "mz:ARGS|BODY|$URL:/foo";` is interpreted as _pattern 'Y' will be matched against *any* GET and POST arguements as long as URL is `/foo`_
|
||||
|
||||
In whitelist context, *all* conditions must be satisfied, so a whitelist like `BasicRule wl:X "mz:$ARGS_VAR:foo|$URL:/bar";` is interpreted as _id X is whitelisted in GET variable `foo` on URL `/bar`_
|
||||
|
||||
> ⚠️ Warning
|
||||
>
|
||||
> **You CANNOT mix `$URL_X:<regex>` and `$ARGS_VAR:<string>`, `$BODY_VAR:<string>` and `$HEADERS_VAR:<string>` in a rule or whitelist.**
|
||||
>
|
||||
> It is allowed instead to mix `$URL_X:<regex>` with `$ARGS_VAR_X:<regex>`, `$BODY_VAR_X:<regex>` and `$HEADERS_VAR_X:<regex>` and to mix `$URL:<string>` with `$ARGS_VAR:<string>`, `$BODY_VAR:<string>` and `$HEADERS_VAR:<string>`.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> It is allowed to mix `FILE_EXT` and `RAW_BODY` with `$URL_X:<regex>` and `$URL:<string>`.
|
||||
|
||||
# Go Back
|
||||
|
||||
[Table of Contents](index.md).
|
||||
@@ -0,0 +1,18 @@
|
||||
## Contributing to naxsi
|
||||
|
||||
We're happily taking patches, contributions, improvements, please see below :
|
||||
|
||||
### Naxsi Core Requirements (runtime)
|
||||
|
||||
* Unit tests with a decent (>90%) coverage
|
||||
* Peer review by core devs in terms of quality/security impacts
|
||||
* For all runtime code, please fuzz the code (see `fuzz` Makefile's target)
|
||||
* For all runtime code, merge will require that we get clean static analysis results (coverity)
|
||||
|
||||
### NxTool/NxAPI
|
||||
|
||||
* Peer review by other developers
|
||||
* General interest for the feature
|
||||
|
||||
Thanks in advance !
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
You can list which IP or CIDR (i.e 192.168.0.1/24), should be always allowed and never blocked.
|
||||
|
||||
IP is taken from `X-Forwarder-for` header (when available or from the client IP).
|
||||
|
||||
Your load balancer, some proxy could add it already, but if not you can add it with `proxy_set_header X-Forwarded-For $remote_addr;`
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
IgnoreIP "1.2.3.4";
|
||||
IgnoreCIDR "192.168.0.0/24";
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root /var/www/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
```
|
||||
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 679 B |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
@@ -0,0 +1,31 @@
|
||||
# Checkrules
|
||||
|
||||
CheckRules instruct naxsi to take an action (`LOG`, `BLOCK`, `DROP`, `ALLOW`) based on a specific score associated to the request. This _score_ has usually been set by one or several [rule(s)](rules-bnf.md).
|
||||
|
||||
`CheckRule` must be present at location level.
|
||||
|
||||

|
||||
|
||||
### Basic Usage
|
||||
|
||||
A typical `CheckRule` is :
|
||||
|
||||
```
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
```
|
||||
|
||||
If the `$SQL` is equal or superior to '8', apply BLOCK flag to the request. Request will only be blocked if location is _not_ in learning mode.
|
||||
|
||||
### Other Usages
|
||||
|
||||
`CheckRule(s)` can as well be used to mix white and black-lists.
|
||||
Having a configuration mixing virtual-patching (see [rules](rules-bnf.md)) and `naxsi_core.rules`, it is possible to have :
|
||||
|
||||
```
|
||||
CheckRule "$UWA >= 4" DROP;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
```
|
||||
|
||||
Thus - even in learning mode - any request with `$UWA` score equal to 4 will block the requests, while requests with `$XSS` score (even superior to 8) will only be blocked on location(s) *not* in learning.
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Directives
|
||||
|
||||
## DeniedUrl
|
||||
* alias: denied_url
|
||||
* context: location
|
||||
|
||||
`DeniedUrl` is a directive that indicates where naxsi will redirect (nginx's internal redirect) blocked requests.
|
||||
|
||||
As the request might be modified during redirect (url & arguments), extra http headers orig_url (original url),
|
||||
orig_args (original GET args) and naxsi_sig (NAXSI_FMT) are added.
|
||||
|
||||
The headers that are forwarded to the location denied page are :
|
||||
|
||||
NAXSI_HEADER_ORIG_URL "x-orig_url"
|
||||
NAXSI_HEADER_ORIG_ARGS "x-orig_args"
|
||||
NAXSI_HEADER_NAXSI_SIG "x-naxsi_sig"
|
||||
|
||||
example:
|
||||
```
|
||||
location / {
|
||||
...
|
||||
DeniedUrl "/RequestDenied";
|
||||
}
|
||||
|
||||
location /RequestDenied {
|
||||
return 418; #I'm a teapot
|
||||
}
|
||||
```
|
||||
|
||||
## LearningMode
|
||||
* alias: learning_mode
|
||||
* context: location
|
||||
|
||||
`LearningMode` if instructs naxsi to enable learning mode (don't honor `BLOCK` directive) in the location.
|
||||
|
||||
For example:
|
||||
|
||||
```nginx
|
||||
location /a {
|
||||
# request triggering BLOCK score won't be blocked here, but simply logued.
|
||||
LearningMode;
|
||||
}
|
||||
```
|
||||
|
||||
Keep in mind that internal rules (those with an `id` inferior to 1000) will drop the request even in learning mode, because it means that something fishy is going on, since naxsi can't correctly process the request.
|
||||
You can of course apply [whitelist](whitelists-bnf.md) if those are false-positives.
|
||||
|
||||
## SecRulesEnabled
|
||||
* alias: rules_enabled
|
||||
* context: location
|
||||
|
||||
`SecRulesEnabled` is a mandatory keyword to enable naxsi in a location.
|
||||
|
||||
## SecRulesDisabled
|
||||
* alias: rules_disabled
|
||||
* context: location
|
||||
|
||||
`SecRulesDisabled` can be used to explicitely disable naxsi in a location.
|
||||
|
||||
## CheckRule
|
||||
* alias: check_rule
|
||||
* context: location
|
||||
|
||||
See [CheckRule](checkrules-bnf.md)
|
||||
|
||||
## BasicRule
|
||||
* alias: basic_rule
|
||||
* context: location
|
||||
|
||||
A directive used to declare a [rule](rules-bnf.md) or a [whitelist](whitelist-bnf.md).
|
||||
|
||||
## MainRule
|
||||
* alias: main_rule
|
||||
* context: http
|
||||
|
||||
A directive used to declare a [rule](rule-bnf.md) or a [whitelist](whitelist-bnf.md).
|
||||
|
||||
## LibInjectionXss
|
||||
* alias: libinjection_xss
|
||||
* context: location
|
||||
|
||||
A directive to enable [libinjection's xss detection](libinjection-integration.md) on *all* part of the http request.
|
||||
|
||||
## LibInjectionSql
|
||||
* alias: libinjection_sql
|
||||
* context: location
|
||||
|
||||
A directive to enable [libinjection's sqli detection](libinjection-integration.md) on *all* part of the http request.
|
||||
|
||||
## naxsi_extensive_log
|
||||
* context: server
|
||||
|
||||
A flag that can be set at [runtime](runtime-modifiers.md) to enable [naxsi extensive logs](naxsilogs.md#naxsi_exlog).
|
||||
|
||||
```
|
||||
server {
|
||||
...
|
||||
|
||||
if ($remote_addr = "1.2.3.4") {
|
||||
set $naxsi_extensive_log 1;
|
||||
}
|
||||
|
||||
location / {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## naxsi_json_log
|
||||
* context: server
|
||||
|
||||
Enable JSON in logs. Examples:
|
||||
|
||||
```
|
||||
# normal log in JSON format
|
||||
2022/12/22 20:36:35 [error] 1189262#0: *1 {"ip":"127.0.0.1","server":"localhost","uri":"/a","config":"block","rid":"a0333f697ff8f12b6a200a24117ff320","cscore0":"$SQL","score0":"8","cscore1":"$XSS","score1":"8","zone0":"ARGS","id0":"1001","var_name0":"b"}, client: 127.0.0.1, server: localhost, request: "GET /a?b="\dasdasdasdadsa HTTP/1.1", host: "localhost"
|
||||
# extended log in json format
|
||||
2022/12/22 20:36:35 [error] 1189262#0: *1 {"ip":"127.0.0.1","server":"localhost","uri":"/a","config":"block","rid":"a0333f697ff8f12b6a200a24117ff320","cscore0":"$SQL","score0":"8","cscore1":"$XSS","score1":"8","zone0":"ARGS","id0":"1001","var_name0":"b"}, client: 127.0.0.1, server: localhost, request: "GET /a?b="\dasdasdasdadsa HTTP/1.1", host: "localhost"
|
||||
```
|
||||
|
||||
TODO DOCUMENTATION
|
||||
|
||||
## naxsi_flag_enable
|
||||
* context: server
|
||||
|
||||
A flag that can be set at [runtime](runtime-modifiers.md) to enable or disable naxsi.
|
||||
|
||||
```
|
||||
server {
|
||||
set $naxsi_flag_enable 1;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## naxsi_flag_learning
|
||||
* context: server
|
||||
|
||||
A flag that can be set at [runtime](runtime-modifiers.md) to enable or disable learning.
|
||||
|
||||
```
|
||||
server {
|
||||
set $naxsi_flag_learning 1;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## naxsi_flag_libinjection_sql
|
||||
* context: server
|
||||
|
||||
A flag that can be set at [runtime](runtime-modifiers.md) to enable or disable [libinjection's sql detection](libinjection-integration.md)
|
||||
|
||||
```
|
||||
server {
|
||||
set $naxsi_flag_libinjection_sql 1;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## naxsi_flag_libinjection_xss
|
||||
|
||||
A flag that can be set at [runtime](runtime-modifiers.md) to enable or disable [libinjection's xss detection](libinjection-integration.md)
|
||||
|
||||
```
|
||||
server {
|
||||
set $naxsi_flag_libinjection_xss 1;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
1. Setup
|
||||
- [compiling nginx+naxsi](naxsi-compile.md)
|
||||
- [Basic nginx/naxsi configuration](naxsi-setup.md)
|
||||
2. Naxsi Configuration Directives
|
||||
- [whitelists](whitelists-bnf.md)
|
||||
- [rules](rules-bnf.md)
|
||||
- [checkrules](checkrules-bnf.md)
|
||||
- [requestdenied](requestdenied-bnf.md)
|
||||
- [naxsi directives index](directives.md)
|
||||
- [zoom : matchzones](matchzones-bnf.md)
|
||||
3. Naxsi Extras
|
||||
- [Raw Body Parsing](rawbody.md)
|
||||
- [libinjection integration](libinjection-integration.md)
|
||||
- [json support](json.md)
|
||||
- [runtime modifiers](runtime-modifiers.md)
|
||||
4. Examples
|
||||
- [whitelists examples](whitelists-examples.md)
|
||||
- [rules examples](rules-examples.md)
|
||||
5. Going deeper
|
||||
- [Understanding naxsi logs](naxsilogs.md)
|
||||
- [Runtime Modifiers](runtime-modifiers.md)
|
||||
- [Naxsi internal rules](internal-rules.md)
|
||||
- [Contributing to naxsi](Contributing.md)
|
||||
6. Integration
|
||||
- [Fail2Ban integration](integration-fail2ban.md)
|
||||
- [AppArmor profile for naxsi](integration-apparmor.md)
|
||||
8. Legacy/old pages
|
||||
- [LEGACY WIKI](legacy/legacy.md)
|
||||
- [Old FAQ](legacy/olds-faq.md)
|
||||
- [Old Vulnerability management](olds-Security-Advisories.md)
|
||||
- Old Naxsi rules mamagement
|
||||
- [installing nxapi](https://github.com/nbs-system/naxsi/tree/master/nxapi.md) (**deprecated**)
|
||||
- [nxapi/nxtool](https://github.com/nbs-system/naxsi/tree/master/nxapi.md) (**deprecated**)
|
||||
- [spike](http://github.com/nbs-system/spike.md) (**deprecated**)
|
||||
@@ -0,0 +1,118 @@
|
||||
# Introduction
|
||||
If you care about process access control on a [AppArmor]( http://apparmor.net ) enabled *GNU/Linux* platform, and you are using Naxsi, this article is for you.
|
||||
|
||||
Of course defining a AppArmor profile is not straight forward and must be customized. AppArmor developers know that and provided tools easing the creation of application profiles.
|
||||
|
||||
Of course, path to files may change depending on your local setup.
|
||||
|
||||
# Requirements
|
||||
|
||||
You basically need:
|
||||
* A GNU/Linux platform with kernel support for AppArmor, [Ubuntu]( http://www.ubuntu.com/ ) for example
|
||||
* `apt-get install apparmor-utils`
|
||||
* A running Nginx/Naxsi
|
||||
* A couple of minutes
|
||||
|
||||
# Generating the profile
|
||||
|
||||
As root, run the following command to generate a profile:
|
||||
|
||||
```
|
||||
aa-genprof /usr/local/sbin/nginx
|
||||
```
|
||||
|
||||
Then answer the few questions:
|
||||
|
||||
```
|
||||
Connecting to repository.....
|
||||
|
||||
WARNING: Error fetching profiles from the repository:
|
||||
RPC::XML::Client::send_request: HTTP server error: Not Found
|
||||
|
||||
Writing updated profile for /usr/local/sbin/nginx.
|
||||
Setting /usr/local/sbin/nginx to complain mode.
|
||||
|
||||
Please start the application to be profiled in
|
||||
another window and exercise its functionality now.
|
||||
|
||||
Once completed, select the "Scan" button below in
|
||||
order to scan the system logs for AppArmor events.
|
||||
|
||||
For each AppArmor event, you will be given the
|
||||
opportunity to choose whether the access should be
|
||||
allowed or denied.
|
||||
|
||||
Profiling: /usr/local/sbin/nginx
|
||||
|
||||
[(S)can system log for SubDomain events] / (F)inish
|
||||
```
|
||||
|
||||
Now stop, start, reload, restart Nginx in order to generate apparmor logs. Then press 'S'
|
||||
|
||||
```
|
||||
Reading log entries from /var/log/messages.
|
||||
Updating AppArmor profiles in /etc/apparmor.d.
|
||||
Complain-mode changes:
|
||||
|
||||
Profile: /usr/local/sbin/nginx
|
||||
Path: /etc/nginx/mime.types
|
||||
Mode: owner r
|
||||
Severity: unknown
|
||||
|
||||
[1 - /etc/nginx/mime.types]
|
||||
|
||||
[(A)llow] / (D)eny / (G)lob / Glob w/(E)xt / (N)ew / Abo(r)t / (F)inish / (O)pts
|
||||
```
|
||||
|
||||
Now we are starting to profile the program and it is then really up to you to decide which file need to be accessed, with which access rights, by Nginx.
|
||||
|
||||
Once done with answering those questions, a apparmor profile for `/usr/local/sbin/nginx` will be created in `/etc/apparmor.d/usr.local.sbin.nginx`.
|
||||
|
||||
Here is mine:
|
||||
|
||||
```apparmor
|
||||
# Last Modified: Wed Jul 25 11:33:59 2012
|
||||
#include <tunables/global>
|
||||
|
||||
/usr/local/sbin/nginx {
|
||||
#include <abstractions/apache2-common>
|
||||
#include <abstractions/base>
|
||||
#include <abstractions/nameservice>
|
||||
|
||||
capability dac_override,
|
||||
capability dac_read_search,
|
||||
capability setgid,
|
||||
capability setuid,
|
||||
|
||||
owner /etc/nginx/** r,
|
||||
owner /etc/ssl/openssl.cnf r,
|
||||
/var/lib/certificates/* r,
|
||||
owner /var/log/nginx/* a,
|
||||
/var/log/nginx/* w,
|
||||
owner /var/run/nginx.pid rw,
|
||||
/var/www/** r,
|
||||
}
|
||||
```
|
||||
|
||||
# Updating the profile for some time
|
||||
Unless you precisely know what the program is doing it is a good idea to let it live for some time and gather the logs that will improve your Apparmor profile.
|
||||
|
||||
So we activate the complain mode of AppArmor:
|
||||
|
||||
aa-complain /etc/apparmor.d/usr.local.sbin.nginx
|
||||
|
||||
And after some time (depending on your usage of Nginx):
|
||||
|
||||
aa-logprof /etc/apparmor.d/usr.local.sbin.nginx
|
||||
|
||||
This command will simply go through the logs and ask you about updating Nginx profile.
|
||||
|
||||
# Enforcing the profile and open up Naxsi to tougher guys
|
||||
|
||||
aa-enforce /etc/apparmor.d/usr.local.sbin.nginx
|
||||
|
||||
Yep that's all!
|
||||
|
||||
BUT! Unauthorized access caught by AppArmor in enforce mode will NOT generate you extra logs! For that you need the “audit” mode (aa-audit). Sadly the audit mode also logs authorized access so it can rapidly become unreadable (you read dmesg output all the time don't you?)
|
||||
|
||||
Enjoy Naxsi and Apparmor!
|
||||
@@ -0,0 +1,40 @@
|
||||
### Introduction
|
||||
|
||||
[Fail2ban]( http://fail2ban.org ) is a nice piece of software allowing you to act on the IP address of someone abusing you, usually banning him using [netfilter]( http://netfilter.org ). Basically you want to ban all skids bruteforcing you SSH service or your webmail login form.
|
||||
|
||||
We figured out that *dropping skids^Whackers is cool, banning them is even better*. So this howto will show you how to ban those who are appearing to much in your naxsi logs.
|
||||
|
||||
# Requirements
|
||||
* An OS running Fail2ban and Naxsi
|
||||
* A few minutes
|
||||
|
||||
# Configuration
|
||||
Very simple, create `/etc/fail2ban/filter.d/nginx-naxsi.conf` with:
|
||||
|
||||
```ini
|
||||
[INCLUDES]
|
||||
before = common.conf
|
||||
[Definition]
|
||||
failregex = NAXSI_FMT: ip=<HOST>&server=.*&uri=.*&learning=0
|
||||
NAXSI_FMT: ip=<HOST>.*&config=block
|
||||
ignoreregex = NAXSI_FMT: ip=<HOST>.*&config=learning
|
||||
```
|
||||
|
||||
And add a section within `/etc/fail2ban/jail.conf` with:
|
||||
|
||||
```ini
|
||||
[nginx-naxsi]
|
||||
enabled = true
|
||||
port = http,https
|
||||
filter = nginx-naxsi
|
||||
logpath = /var/log/nginx/*error.log
|
||||
maxretry = 6
|
||||
```
|
||||
|
||||
So in `/var/log/fail2ban.log`, any time the same IP triggers Naxsi 6 times in 5 minutes (`fail2ban findtime=600`) you should see:
|
||||
|
||||
```
|
||||
2012-06-29 15:34:44,016 fail2ban.actions: WARNING [nginx-naxsi] Ban 88.z.x.y`
|
||||
```
|
||||
|
||||
BONUS: Graph this new fail2ban jail with [Munin]( http://munin-monitoring.org/ ) so you can track how many guys are trying to abuse your website :)
|
||||
@@ -0,0 +1,109 @@
|
||||
# Internal rules
|
||||
|
||||
Internal rules are rules that can be fired by naxsi, when request is incorrect or extremely unusual - or naxsi is not able to parse the request (ie. unknown content-type).
|
||||
Please note that those rules do not set an internal score, but usually just set the `block` flag of the request to `1`.
|
||||
|
||||
You can whitelist those, but you should never have to do so.
|
||||
When whitelisting an internal rule, you might be disabling naxsi at least partially, so think twice about it.
|
||||
|
||||
|
||||
|
||||
|
||||
## weird_request
|
||||
* id: 1
|
||||
* action: block
|
||||
* impact: pass-thru
|
||||
|
||||
A request that cannot be understood by naxsi.
|
||||
When whitelisting this one, you are telling naxsi to blindly accept the request and not to parse it.
|
||||
|
||||
## big_request
|
||||
* id: 2
|
||||
* action: block
|
||||
* impact : pass-thru
|
||||
|
||||
A request that is buffered on file system because it's too big.
|
||||
Naxsi doesn't parse buffered requests. You can always increase [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) in nginx's config.
|
||||
|
||||
## uncommon_hex_encoding
|
||||
* id: 10
|
||||
* action: block
|
||||
* impact : partial loss of decoding
|
||||
|
||||
Hex encoding that is not valid, and that naxsi cannot "url decode".
|
||||
|
||||
## uncommon_content_type
|
||||
* id: 11
|
||||
* action: block
|
||||
* impact : pass-thru on BODY
|
||||
|
||||
A content-type unknown to naxsi. Meaning naxsi cannot parse the body.
|
||||
However, if id:11 is whitelisted and >= 0.55rc2, [RAW_BODY](rawbody) rules can be used.
|
||||
|
||||
## uncommon_url
|
||||
* id: 12
|
||||
* action: block
|
||||
* impact: partial pass-thru on GET args
|
||||
|
||||
An URL that is not standard (ie. `?x=foo&z=bar`). Can lead to uncorrectly parsed arguments when whitelisted.
|
||||
|
||||
|
||||
# uncommon_post_format
|
||||
* id: 13
|
||||
* action: block
|
||||
* impact: pass-thru on BODY
|
||||
|
||||
POST body is malformed, ie.
|
||||
* bad content-disposition
|
||||
* no variable name
|
||||
* malformed attached file content-type
|
||||
|
||||
## uncommon_post_boundary
|
||||
* id: 14
|
||||
* action: block
|
||||
* impact: pass-thru on BODY
|
||||
|
||||
POST body is malformed, ie.
|
||||
* bad content-type
|
||||
* bad boundary (too short, too long, not rfc compliant)
|
||||
|
||||
## invalid_json
|
||||
* id: 15
|
||||
* action: block
|
||||
* impact: pass-thru on BODY (json)
|
||||
|
||||
JSON is malformed (ie. missing `} ]`).
|
||||
|
||||
|
||||
## empty_body
|
||||
* id: 16
|
||||
* action: block
|
||||
* impact: pass-thru on BODY
|
||||
|
||||
Raised when body is empty and/or content-length is zero.
|
||||
|
||||
|
||||
## libinjection_sql
|
||||
* id: 17
|
||||
* action: ??
|
||||
|
||||
See [libinjection](libinjection-integration.md).
|
||||
|
||||
## libinjection_xss
|
||||
* id: 18
|
||||
* action: ??
|
||||
|
||||
See [libinjection](libinjection-integration.md).
|
||||
|
||||
## no_rules
|
||||
* id: 19
|
||||
* action: drop
|
||||
* impact: no rules checked
|
||||
|
||||
Raised when naxsi isn't configured with any MainRules.
|
||||
|
||||
## bad_utf8
|
||||
* id: 20
|
||||
* action: drop
|
||||
|
||||
Raised when [surrogate utf8](https://tools.ietf.org/html/rfc3629) is detected.
|
||||
@@ -0,0 +1,31 @@
|
||||
# JSON
|
||||
|
||||
|
||||
POST/PUT request with content-type `application/json` will be handled by naxsi so that it should be transparent in the whitelist / signatures writting process :
|
||||
* all rules targeting `BODY` are applied to json content as well
|
||||
* whitelists (or rules) for specific variable use the classic `$BODY_VAR:xx`
|
||||
|
||||
|
||||
However for JSON, naxsi does not keep track of depth, and has [a hardcoded limit of 10 (depth)](internal-rules.md#invalid_json).
|
||||
|
||||
A request :
|
||||
```
|
||||
POST ...
|
||||
|
||||
{
|
||||
"this" : { "will" : ["work", "does"],
|
||||
"it" : "??" },
|
||||
"tr<igger" : {"test_1234" : ["foobar", "will", "trigger", "it"]}
|
||||
}
|
||||
```
|
||||
|
||||
A rule that will match :
|
||||
```
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
```
|
||||
|
||||
The associated whitelist :
|
||||
```
|
||||
BasicRule wl:X "mz:$BODY_VAR:test_1234";
|
||||
```
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
**NOTE**
|
||||
|
||||
This is the legacy wiki page.
|
||||
Everything here should be considered obsolete.
|
||||
|
||||
If you find anything here (that is not in *current* wiki),
|
||||
please notify us via issues or help us moving it to current wiki.
|
||||
|
||||
|
||||
|
||||
* [fail2ban](olds-A-fail2ban-profile-for-Naxsi.md)
|
||||
* [naxsilogs](olds-naxsilogs.md)
|
||||
* [naxsivsobfuscated](olds-naxsivsobfuscated.md)
|
||||
* [deniedurl](olds-deniedurl.md)
|
||||
* [Knownbugs](olds-Knownbugs.md)
|
||||
* [rulessyntax](olds-rulessyntax.md)
|
||||
* [whitelists](olds-whitelists.md)
|
||||
* [basicsetup](olds-basicsetup.md)
|
||||
* [Security-Advisories](olds-Security-Advisories.md)
|
||||
* [libinjection](olds-libinjection.md)
|
||||
* [dynamicmodifiers](olds-dynamicmodifiers.md)
|
||||
* [installation](olds-installation.md)
|
||||
* [Philosophy](olds-Philosophy.md)
|
||||
* [faq](olds-faq.md)
|
||||
* [Home](olds-Home.md)
|
||||
* [naxsivsappscan](olds-naxsivsappscan.md)
|
||||
* [embedded_rules](olds-embedded_rules.md)
|
||||
* [testing-and-stuff](olds-testing-and-stuff.md)
|
||||
@@ -0,0 +1,30 @@
|
||||
Welcome to naxsi wiki !
|
||||
* [[Understand naxsi's philosophy & design|philosophy]] (READ FIRST)
|
||||
|
||||
### Tutorials
|
||||
* [[Installing Naxsi|installation]]
|
||||
* [[Basic setup|BasicSetup]]
|
||||
* [[Naxsi on steroids (lua/ngx/* scripting)|dynamicmodifiers]]
|
||||
* [[FAQ|FAQ]]
|
||||
|
||||
### Understanding Naxsi
|
||||
* [[Internal and core rules|embedded_rules]]
|
||||
* [[Understand Naxsi Whitelists|Whitelists]]
|
||||
* [[Understanding naxsi's rules syntax|RulesSyntax]]
|
||||
* [[Understanding Naxsi exceptions|NaxsiLogs]]
|
||||
* [[DeniedUrl & post_action|DeniedUrl]]
|
||||
* [[Libinjection integration|Libinjection]]
|
||||
|
||||
|
||||
### Learning tools
|
||||
* [nxapi]( https://github.com/nbs-system/naxsi/tree/master/nxapi )
|
||||
|
||||
### Extras
|
||||
* [[Naxsi behaviour against obfuscated/complex payloads|naxsivsobfuscated]]
|
||||
* [[Naxsi behaviour against web app scanner|naxsivsappscan]]
|
||||
* [[A fail2ban profile for Naxsi|A-fail2ban-profile-for-Naxsi]]
|
||||
* [[How to create an AppArmor profile for Naxsi|How-to-create-an-Apparmor-profile-for-Naxsi]]
|
||||
* [[Unit tests, code coverage…|testing-and-stuff]]
|
||||
|
||||
### Issues
|
||||
* [[Naxsi known bugs and limitations|Knownbugs]]
|
||||
@@ -0,0 +1,7 @@
|
||||
Naxsi core :
|
||||
|
||||
* Naxsi only parses GET/POST/PUT requests.
|
||||
* Naxsi supports a restricted kind of body content-type parsing : application/x-www-form-urlencoded, multipart/form-data, application/json. Other content-types are not supported.
|
||||
* Naxsi does not support parsing of a file buffered to disk by nginx: [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size)
|
||||
* You can't use `|` caracter in your matchzone regular expressions.
|
||||
* HTTPv2 is currently not supported: [issue #227](https://github.com/nbs-system/naxsi/issues/227 )
|
||||
@@ -0,0 +1,24 @@
|
||||
## Naxsi overview
|
||||
|
||||
Technically, *naxsi* is a third party [nginx]( https://nginx.org ) module, available as a package for many UNIX-like platforms. This module, by default, reads a small subset of [simple rules]( https://github.com/nbs-system/naxsi/blob/master/naxsi_config/naxsi_core.rules ) containing known patterns involved in websites vulnerabilities and exploitation. For example, `<`, `|` or `drop` are not supposed to be part of a URI.
|
||||
|
||||
Being very simple, those patterns may match legitimate queries, it is naxsi's administrator duty to add specific rules that will [[whitelist|whitelists]] those legitimate behaviours. The administrator can either add whitelists manually by analysing nginx's error log, or (recommended) start the project by an intensive auto-learning phase that will automatically generate whitelisting rules regarding website's behaviour.
|
||||
|
||||
In short, Naxsi behaves like a *DROP-by-default* firewall, the only job needed is to add required ACCEPT rules for the target website to work properly.
|
||||
|
||||
## Naxsi expected usage
|
||||
|
||||
As Naxsi relies on whitelists to avoid false positives (even more than with a "classic" WAF), it comes with a tool called [nxapi]( https://github.com/nbs-system/naxsi/tree/master/nxapi ) that helps the administrator to create the appropriate whitelists for an application.
|
||||
If you are using common web applications 'out of the box', you might as well find pre-generated whitelists [here]( https://github.com/nbs-system/naxsi-rules )
|
||||
|
||||
## Naxsi Pro's and Con's
|
||||
Naxsi relies on a positive model, which has multiple advantages, but comes with some extra constraints :
|
||||
|
||||
#### Pros
|
||||
* **Fast** : Minimalist, Lightweight runtime processing
|
||||
* **Resilient** : Signature-less design allows improved resilience against [[obfuscated / complex attacks|NaxsiVSObfuscated]]
|
||||
* **Update independent** : Signature-less design allows sustainable security, even without updates
|
||||
|
||||
#### Con's
|
||||
* **Configuration** : Positive approach requires a heavier whitelisting process than negative model
|
||||
* **Not Idiot Proof** : As naxsi behaves like a network firewall, if you are setting up too laxist rules, your web application will not be protected correctly.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Security Advisories
|
||||
_Because nobody's perfect and coding while drunk is dangerous !_
|
||||
|
||||
## Reporting Vulnerabilities and Security Issues
|
||||
|
||||
[As stated on the project's homepage](https://github.com/nbs-system/naxsi#security-issues), you can send me an email directly. Or, you can use the [issues](https://github.com/nbs-system/naxsi/issues)
|
||||
|
||||
## NO-CVE: [Medium-Low] Potential bypass on SQL keywords for IIS/ASP
|
||||
* Rated : Medium-Low
|
||||
* Date : 26 March 2013
|
||||
* Affected : All
|
||||
* Fixed in : 0.50-1 [r565 (on Google code)](https://code.google.com/p/naxsi/source/detail?r=565)
|
||||
* Discovered by : Safe3
|
||||
* References : [http://seclists.org/bugtraq/2013/Mar/133](http://seclists.org/bugtraq/2013/Mar/133)
|
||||
|
||||
Naxsi does not suppress/decode incorrectly url-encoded characters. On the other hand, IIS/ASP will treat "s%e%l%e%c%t" as "select", while naxsi will still see it as "s%e%l%e%c%t", thus rendering SQL keywords rule ineffective. Only the SQL-keywords rule is affected, thus the exploitation window is limited to quote-less, two-fields (max) SQL injections.
|
||||
|
||||
## CVE-2012-3380: [Medium] Potential file disclosure in naxsi's nx_extract
|
||||
* Rated: Medium
|
||||
* Date 18 May 2012
|
||||
* Affected: 0.46
|
||||
* Fixed in: 0.46-1
|
||||
* Discovered by : Naxsi dev team
|
||||
* References: [oss-security](http://seclists.org/oss-sec/2012/q3/12) [Secunia](https://secunia.com/advisories/49811) [Securelist](https://www.securelist.com/en/advisories/49811)
|
||||
|
||||
Local includes in nx_extract are not properly filtered, allowing a remote attacker to disclose files local to nx_extract.
|
||||
More details [https://code.google.com/p/naxsi/source/detail?r=307](https://code.google.com/p/naxsi/source/detail?r=307)
|
||||
|
||||
## NO-CVE: [Low] SQL Injection in naxsi's nx_intercept
|
||||
* Rated: Low
|
||||
* Date: 2 Apr 2012
|
||||
* Affected: 0.44
|
||||
* Fixed in: 0.44-1
|
||||
* Discovered by : Naxsi dev team
|
||||
|
||||
Ironically, an SQL Injection is present in naxsi's new python learning daemon (nx_intercept.py). The vulnerability is rated as low, as:
|
||||
* Learning daemon is usually restricted to trusted IPs
|
||||
* No sensitive data are present into database, as it is only used to store exceptions.
|
||||
The vulnerabilty only affects the nx_intercept python daemon, not the naxsi's core. Vulnerable code is:
|
||||
```python
|
||||
if md5 is not None and ip is not None:
|
||||
cursor.execute("INSERT INTO http_monitor (peer_ip, md5) VALUES ('%s', '%s')" % (ip, md5))
|
||||
return
|
||||
```
|
||||
@@ -0,0 +1,86 @@
|
||||
### Initial setup
|
||||
|
||||
Let's take the first step to use : setting up learning mode for your website!
|
||||
This page assumes you already know how to properly configure *nginx* without Naxsi and get it working.
|
||||
|
||||
```nginx
|
||||
#/etc/nginx/nginx.conf :
|
||||
user www-data;
|
||||
worker_processes 1;
|
||||
worker_rlimit_core 500M;
|
||||
working_directory /tmp/;
|
||||
error_log /var/log/nginx/error.log;
|
||||
pid /var/run/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
# multi_accept on;
|
||||
}
|
||||
http {
|
||||
include /etc/nginx/naxsi_core.rules;
|
||||
include /etc/nginx/mime.types;
|
||||
server_names_hash_bucket_size 128;
|
||||
access_log /var/log/nginx/access.log;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
tcp_nodelay on;
|
||||
gzip on;
|
||||
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
```
|
||||
|
||||
Notice the inclusion of the `/etc/nginx/naxsi_core.rules` file. This is the only thing you need to add to your existing `http {}` section if you already have a configuration. The [naxsi_core.rules]( https://github.com/wargio/naxsi/blob/main/naxsi_rules/naxsi_core.rules ) file is provided, and contains Naxsi core rules. As you might notice, these are not signatures, in the classic WAF sense, but simple "score rules", for example:
|
||||
|
||||
```
|
||||
MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001;
|
||||
```
|
||||
|
||||
You can see more about rules syntax byt taking a look at the [[rules syntac documentation|rulessyntax]]
|
||||
Now, let's have a look at my /etc/nginx/site-enabled/default :
|
||||
|
||||
```nginx
|
||||
server {
|
||||
proxy_set_header Proxy-Connection "";
|
||||
listen *:80;
|
||||
access_log /tmp/nginx_access.log;
|
||||
error_log /tmp/nginx_error.log debug;
|
||||
location / {
|
||||
include /etc/nginx/naxsi.rules;
|
||||
proxy_pass http://x.x.x.x/;
|
||||
proxy_set_header Host www.mysite.com;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 418;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The naxsi's configuration itself is in the file `/etc/nginx/naxsi.rules`:
|
||||
|
||||
```ini
|
||||
LearningMode; # Enables learning mode
|
||||
SecRulesEnabled;
|
||||
#SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
|
||||
## check rules
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$EVADE >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
```
|
||||
|
||||
With the following setup :
|
||||
* Naxsi will be enabled
|
||||
* Naxsi will not block any requests (while LearningMode is active)
|
||||
* To-be-blocked requests will generate event logs in your location's error.log file
|
||||
|
||||
If you issue a request to `http://127.0.0.1/?a=<`, you'll get something like this in your logs:
|
||||
|
||||
```
|
||||
2013/05/30 20:09:43 [error] 8404#0:*3 NAXSI_FMT: ip=127.0.0.1&server=127.0.0.1&uri=/&learning=0&vers=0.50&total_processed=3&total_blocked=1&zone0=ARGS&id0=1302&var_name0=a, client: 127.0.0.1, server: , request: "GET /?a=< HTTP/1.0", host: "127.0.0.1"
|
||||
```
|
||||
|
||||
Once you get this kind of lines in your error log, you have naxsi running in LearningMode, congrats! You can now move on to [[generating whitelists|whitelists]]!
|
||||
@@ -0,0 +1,19 @@
|
||||
**DeniedUrl** is used to indicate a location where blocked requests will be redirected (internally).
|
||||
|
||||
In version before 0.49, by default, naxsi forwards blocked request there while in learning mode. Upon "real" request termination, using nginx's [post_action]( http://wiki.nginx.org/HttpCoreModule#post_action ) mechanism.
|
||||
|
||||
This was due to usage of `nx_intercept`, which could intercept learning traffic in live.
|
||||
|
||||
As the request might be modified during redirect (url & arguments), extra http headers `orig_url` (original url), `orig_args` (original GET args) and `naxsi_sig` (NAXSI_FMT) are added.
|
||||
|
||||
If $naxsi_flag_post_action is set to "1", naxsi will perform post_action (while in learning) even in versions '''> 0.49'''.
|
||||
|
||||
|
||||
The headers that are forwarded to the location denied page are :
|
||||
|
||||
```
|
||||
./naxsi_runtime.c: #define NAXSI_HEADER_ORIG_URL "x-orig_url"
|
||||
./naxsi_runtime.c: #define NAXSI_HEADER_ORIG_ARGS "x-orig_args"
|
||||
./naxsi_runtime.c: #define NAXSI_HEADER_NAXSI_SIG "x-naxsi_sig"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
### naxsi dynamic configuration (aka nginx vars)
|
||||
|
||||
Since [0.49]( https://github.com/wargio/naxsi/tree/0.49 ), naxsi supports a limited set of boolean variables that can be set to `0` or `1` to override or modify its behaviour.
|
||||
|
||||
* `naxsi_flag_learning` can override Naxsi learning flag.
|
||||
* `naxsi_flag_post_action` may be used to disable `post_action` in learning mode.
|
||||
* `naxsi_flag_enable` could override naxsi's `SecRulesEnabled`.
|
||||
* `naxsi_extensive_log` can force naxsi to log the `CONTENT` of variable matching rules (see notes at bottom).
|
||||
|
||||
### Gentle reminder
|
||||
It is important to know that naxsi operates at the `REWRITE` phase of nginx. Thus, setting those variables directly in the location where Naxsi is present is ineffective (as naxsi will be called before variable set is effective).
|
||||
|
||||
This is correct:
|
||||
|
||||
```nginx
|
||||
set $naxsi_flag_enable 0;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
But this is wrong:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
set $naxsi_flag_learning 1;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
With that said, you can use the power of nginx, lua, etc. to change naxsi's behaviour. The presence of these variables will enable/disable learning mode, naxsi itself or force extensive logging. You can thus do things naxsi is usually not able to, like modifying its behaviour according to (nginx) variables set at run-time :
|
||||
|
||||
```nginx
|
||||
# Disable naxsi if client ip is 127.0.0.1
|
||||
if ($remote_addr = "127.0.0.1") {
|
||||
set $naxsi_flag_enable 0;
|
||||
}
|
||||
```
|
||||
|
||||
Those variables can as well be set from lua scripts (see nginx's [mod_lua]( https://github.com/openresty/lua-nginx-module#readme )).
|
||||
|
||||
### naxsi_flag_learning
|
||||
|
||||
If the `naxsi_flag_learning` variable is present, this value will override naxsi's current static configuration regarding learning mode.
|
||||
|
||||
```nginx
|
||||
if ($remote_addr = "1.2.3.4") {
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
location / {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### naxsi_flag_post_action
|
||||
|
||||
The [post_action]( http://wiki.nginx.org/HttpCoreModule#post_action ) option can be used by naxsi to literally forward a request to the `DeniedUrl` location. It is on by default until [naxsi 0.50]( https://github.com/wargio/naxsi/tree/0.50 ) (a souvenir from `nx_intercept`) and is off by default since [naxsi 0.51]( https://github.com/wargio/naxsi/tree/0.51 ).
|
||||
|
||||
### naxsi_flag_enable
|
||||
If the `naxsi_flag_enable` variable is present and set to `0`, naxsi will be disabled in this request. This allows you to partially disable naxsi in specific conditions. To completely disable naxsi for "trusted" users :
|
||||
|
||||
```nginx
|
||||
set $naxsi_flag_enable 0;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### naxsi_extensive_log
|
||||
If present and set to `1`, this variable will force naxsi to log the CONTENT of variable matching rules.
|
||||
Because of a potential impact on performance, use this with caution. Naxsi will log those to nginx’s error_log, ie:
|
||||
|
||||
```
|
||||
NAXSI_EXLOG: ip=%V&server=%V&uri=%V&id=%d&zone=%s&var_name=%V&content=%V
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
# Internal rules
|
||||
|
||||
Since its [0.53 release]( https://github.com/wargio/naxsi/tree/0.53 ), naxsi comes with a predefined set of rules with the following id:
|
||||
|
||||
- `1` - "weird request" : This a generic exception used for improperly formatted requests.
|
||||
- `2` - "big request" : Request is too big and has been buffered to disk by nginx.
|
||||
- `10` - "uncommon hex encoding" : Encoding suggests this might be an escape attempt.
|
||||
- `11` - "uncommon content-type" : Content-type of BODY is unknown / cannot be parsed.
|
||||
- `12` - "uncommon URL" : URL is malformed
|
||||
- `13` - "uncommon post format" : malformed boundary or content-disposition
|
||||
- `14` - "uncommon post boundary" : BODY boundary line is malformed, or boundary breaks RFC
|
||||
- `15` - invalid JSON - gets parsed when application/json is detected (experimental as of summer 2014)
|
||||
- `16` - "empty body" : POST with empty BODY, available since [naxsi 0.53-1]( https://github.com/wargio/naxsi/tree/0.53 ), was merged with `id:11` before.
|
||||
- `17` - "Libinjection SQL" : Libinjection SQL detection was triggered.
|
||||
- `18` - "Libinjection XSS" : Libinjection XSS detection was triggered.
|
||||
|
||||
|
||||
## naxsi-core.rules
|
||||
|
||||
Naxsi ships with a [basic core-rule-set]( https://github.com/wargio/naxsi/blob/main/naxsi_rules/naxsi_core.rules )
|
||||
that protects against common attacks. Those Core-Rules should always be loaded.
|
||||
|
||||
- [SQL-Injections]( https://www.owasp.org/index.php/SQL_Injection ) (1000-1099)
|
||||
- Obvious [Remote File Inclusions]( https://www.owasp.org/index.php/OWASP_Periodic_Table_of_Vulnerabilities_-_Remote_File_Inclusion ) (1100-1199)
|
||||
- [Directory Traversal]( https://www.owasp.org/index.php/Path_Traversal ) (1200-1299)
|
||||
- [Cross Site Scripting]( https://www.owasp.org/index.php/XSS ) (1300-1399)
|
||||
- [Basic Evading tricks]( https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet ) (1400-1500)
|
||||
- [File uploads]( https://www.owasp.org/index.php/Unrestricted_File_Upload ) (1500-1600)
|
||||
@@ -0,0 +1,40 @@
|
||||
## Naxsi blocks all requests but no IDs are written
|
||||
|
||||
If you see lines like :
|
||||
```
|
||||
2013/09/03 00:11:45 [error] 14913#0: *1 NAXSI_FMT: ip=127.0.0.1&server=localhost&uri=/&learning=1&vers=0.50&total_processed=1&total_blocked=1, client: 127.0.0.1, server: , request: "GET / HTTP/1.0", host: "localhost"
|
||||
```
|
||||
|
||||
It's usually because you forgot to include naxsi rules in your `http {}` block. Try adding `include /etc/nginx/naxsi_core.rules;` to it.
|
||||
|
||||
## I compiled naxsi from source, I enabled NAXSI, but it doesn't seem to filter requests?
|
||||
|
||||
Did you put naxsi directives first in your location configuration and `./configure`?
|
||||
You **MUST** put naxsi's directives first in your location configuration. Please check as well your configuration at both `http {}` and `location {}` levels.
|
||||
|
||||
## How to limit learning mode to some specific IPs/Users/locations?
|
||||
|
||||
Naxsi supports [[DynamicModifiers]] to change behaviour at runtime.
|
||||
You can as well rely on nginx's [HttpAllowModule]( http://wiki.nginx.org/HttpAccessModule ).
|
||||
You could also set different vhosts (with associated locations) up, and define some to have `learningmode`, and others without.
|
||||
|
||||
## How to test that naxsi is working?
|
||||
|
||||
Setup your RequestDenied as follow :
|
||||
|
||||
```nginx
|
||||
location /RequestDenied {
|
||||
return 500;
|
||||
}
|
||||
```
|
||||
|
||||
Check that `learningMode` is disabled, that `naxsi_core.rules` is included, and issue a request like `http:/.../?a=<>`. You should get a 500 from nginx, and get an entry in your nginx error log, starting with `NAXSI_FMT:`.
|
||||
|
||||
## I downloaded / installed packages from X, but I don't have this feature / new learning tools?
|
||||
Naxsi is a young and evolving project and distributions cannot always keep up. Use the source luke, as documented [[here|installation]].
|
||||
|
||||
## Why do you keep radically changing learning tools?
|
||||
Because it's a not-that-easy problem, and we haven't found a satisfying solution yet. If you have ideas about how we could do it better, please tell us!
|
||||
|
||||
## Can I run naxsi on Windows
|
||||
No one tried that yet, but feel free to go down the [rabbit hole]( http://nginx-win.ecsds.eu/ )
|
||||
@@ -0,0 +1,82 @@
|
||||
You can install nginx+naxsi either from packages (available from official repositories on debian, freebsd, netbsd) or directly from source.
|
||||
As Nginx does not yet support runtime module loading, lot of people will choose compiling from source to avoid package maintainers delay.
|
||||
|
||||
## Installation From Packages
|
||||
|
||||
Packages are available for NetBSD, FreeBSD and Debian, from distribution's repositories.
|
||||
Naxsi project does not create/publish such packages.
|
||||
|
||||
## Installation From Source
|
||||
|
||||
Naxsi should be working with all Nginx versions superior to 0.8.X.
|
||||
To install it from source, we need to fetch both nginx and naxsi sources.
|
||||
|
||||
#### Import Source Signing Keys
|
||||
Import [Nginx signing keys]( http://nginx.org/en/pgp_keys.html ) and [naxsi's one](https://pgp.mit.edu/pks/lookup?op=get&search=0x251A28DE2685AED4).
|
||||
|
||||
_note_ : Signed releases of naxsi started at [0.54rc3]( https://github.com/nbs-system/naxsi/tree/0.54rc3 ).
|
||||
|
||||
```shell
|
||||
gpg --keyserver pgp.mit.edu --recv-keys 0x251A28DE2685AED4 7BD9BF62 A1C052F8
|
||||
```
|
||||
|
||||
#### Download Sources
|
||||
Note: replace `TAG_NAME` with the [tagged release](https://github.com/nbs-system/naxsi/tags) you want.
|
||||
|
||||
```shell
|
||||
wget http://nginx.org/download/nginx-x.x.xx.tar.gz
|
||||
wget http://nginx.org/download/nginx-x.x.xx.tar.gz.asc
|
||||
wget https://github.com/nbs-system/naxsi/archive/TAG_NAME.tar.gz
|
||||
wget https://github.com/nbs-system/naxsi/releases/download/TAG_NAME/TAG_NAME.tar.gz.asc
|
||||
```
|
||||
|
||||
#### Verify Sources
|
||||
|
||||
```shell
|
||||
gpg --verify nginx-*.tar.gz.asc nginx-*.tar.gz
|
||||
gpg --verify TAG_NAME.tar.gz.asc TAG_NAME.tar.gz
|
||||
```
|
||||
|
||||
Both source verifications should present you a message that says:
|
||||
|
||||
```
|
||||
> gpg: Good signature from ...
|
||||
```
|
||||
|
||||
#### Unpack Sources
|
||||
If both sources verify, then it is safe to unpack and proceed to compilation.
|
||||
|
||||
```shell
|
||||
tar -xvzf nginx-*.tar.gz
|
||||
tar -xvzf TAG_NAME.tar.gz
|
||||
```
|
||||
|
||||
#### Compiling
|
||||
Install `libpcre` (and `libssl` if you want https, along with `zlib` for gzip support) with your favorite package manager, naxsi relies on it for regex.
|
||||
|
||||
```shell
|
||||
cd nginx-*
|
||||
./configure --add-module=../naxsi-x.xx/naxsi_src/ [add/remove your favorite/usual options]
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
My personal "building" options being here :
|
||||
|
||||
```shell
|
||||
./configure --conf-path=/etc/nginx/nginx.conf --add-module=../naxsi-x.xx/naxsi_src/ \
|
||||
--error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body \
|
||||
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log \
|
||||
--http-proxy-temp-path=/var/lib/nginx/proxy --lock-path=/var/lock/nginx.lock \
|
||||
--pid-path=/var/run/nginx.pid --with-http_ssl_module \
|
||||
--without-mail_pop3_module --without-mail_smtp_module \
|
||||
--without-mail_imap_module --without-http_uwsgi_module \
|
||||
--without-http_scgi_module --with-ipv6 --prefix=/usr
|
||||
```
|
||||
|
||||
# IMPORTANT
|
||||
|
||||
You need to remember this :
|
||||
NGINX will decide the order of modules according the order of the module's directive in nginx's `./configure`. So, no matter what (except you really know what you are doing) put naxsi first in your `./configure`.
|
||||
|
||||
If you don't do so, you might run into various problems, from random / unpredictable behaviours to non-effective WAF.
|
||||
@@ -0,0 +1,22 @@
|
||||
Libinjection is being integrated into naxsi, starting at 0.54rc0
|
||||
|
||||
* https://github.com/nbs-system/naxsi/releases/tag/0.54rc0
|
||||
|
||||
We are looking for people to test it and provide feedback on it.
|
||||
|
||||
Usage :
|
||||
|
||||
* By default, Libinjection (sql/xss) is disabled, it needs to be enabled at location level using one of these directives : LibInjectionSql / LibInjectionXss (or libinjection_sql / libinjection_xss in nginx style).
|
||||
* It can as well be dynamically enabled / disabled using :
|
||||
```
|
||||
set $naxsi_libinjection_xss 0|1;
|
||||
set $naxsi_libinjection_sql 0|1;
|
||||
```
|
||||
* When triggered, libinjection sql/xss will trigger internal rules :
|
||||
* libinjection_sql, with internal ID of 17, will increase $LIBINJECTION_SQL of 8
|
||||
* libinjection_xss, with internal ID of 18, will increase $LIBINJECTION_XSS of 8
|
||||
|
||||
* If you want to add action uppon trigger, write your CheckRules on those scores ;) (as for other rules, LOG/BLOCK/DROP is allowed!)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
### NAXSI_FMT
|
||||
|
||||
NAXSI_FMT are the lines you can see in your error.log :
|
||||
|
||||
```
|
||||
2013/11/10 07:36:19 [error] 8278#0: *5932 NAXSI_FMT: ip=X.X.X.X&server=Y.Y.Y.Y&uri=/phpMyAdmin-2.8.2/scripts/setup.php&learning=0&vers=0.52&total_processed=472&total_blocked=204&block=0&cscore0=$UWA&score0=8&zone0=HEADERS&id0=42000227&var_name0=user-agent, client: X.X.X.X, server: blog.memze.ro, request: "GET /phpMyAdmin-2.8.2/scripts/setup.php HTTP/1.1", host: "X.X.X.X"
|
||||
```
|
||||
|
||||
Here, client `X.X.X.X` request to server `Y.Y.Y.Y` did trigger the rule `42000227` in the var named `user-agent` in the`HEADERS` zone. `id X` might seem obscure, but you can see the meaning in [naxsi_core.rules]( https://github.com/nbs-system/naxsi/blob/master/naxsi_config/naxsi_core.rules ):
|
||||
|
||||
```
|
||||
MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302;
|
||||
```
|
||||
|
||||
NAXSI_FMT is composed of different items :
|
||||
|
||||
- `ip` : Client's ip
|
||||
- `server` : Requested Hostname (as seen in http header `Host`)
|
||||
- `uri`: Requested URI (without arguments, stops at `?`)
|
||||
- `learning`: tells if naxsi was in learning mode (0/1)
|
||||
- `vers` : Naxsi version, only since [0.51]( https://github.com/nbs-system/naxsi/tree/0.51 )
|
||||
- `total_processed`: Total number of requests processed by nginx's worker
|
||||
- `total_blocked`: Total number of requests blocked by (naxsi) nginx's worker
|
||||
- `zoneN`: Zone in which match happened (see "Zones" in the table below)
|
||||
- `idN`: The rule id that matched
|
||||
- `var_name`: Variable name in which match happened (optional)
|
||||
|
||||
Several groups of zone, id, var_name can be present in a single line.
|
||||
|
||||
Existing zones can be are the following:
|
||||
|
||||
- `ARGS`: GET args
|
||||
- `$ARGS_VAR `: named GET argument
|
||||
- `$ARGS_VAR_X`: regex matching the name of a GET argument
|
||||
- `HEADERS `: HTTP Headers
|
||||
- `$HEADERS_VAR ` : named HTTP header
|
||||
- `$HEADERS_VAR_X`: regex matching a named HTTP header
|
||||
- `BODY `: POST args (and RAW_BODY)
|
||||
- `$BODY_VAR `: named POST argument
|
||||
- `$BODY_VAR_X`: regex matching the name of a POST argument
|
||||
- `URL`: The URL (before '?')
|
||||
- `$URL`: The specified URL
|
||||
- `$URL_X`: regex matching the URL (before '?')
|
||||
- `FILE_EXT`: Filename (in a multipart POST containing a file)
|
||||
|
||||
A zone can be suffixed with "|NAME", meaning the rule matched in the name of the variable, not its content.
|
||||
|
||||
As well, several scores can be present since [0.53]( https://github.com/nbs-system/naxsi/tree/0.53 ):
|
||||
|
||||
```ini
|
||||
cscoreX=[score_tag]
|
||||
scoreX=[score_value]
|
||||
```
|
||||
|
||||
### NAXSI_EXLOG
|
||||
|
||||
NAXSI_EXLOG is a complement to [[naxsilogs]]. Along with exceptions, it contains actual content of the matched request. While NAXSI_FMT only contains IDs and location of exception, NAXSI_EXLOG provides actual content, allowing you to easily decide if it's a false positive or not.
|
||||
|
||||
Learning tools uses this at his advantage. Extensive log is enabled by adding the following line in your server {} section but **out** of your location.
|
||||
|
||||
```javascript
|
||||
set $naxsi_extensive_log 1;
|
||||
```
|
||||
|
||||
This feature is provided by [[DynamicModifiers]].
|
||||
|
||||
```
|
||||
2013/05/30 20:47:05 [debug] 10804#0:*1 NAXSI_EXLOG: ip=127.0.0.1&server=127.0.0.1&uri=/&id=1302&zone=ARGS&var_name=a&content=a<>bcd
|
||||
2013/05/30 20:47:05 [error] 10804#0:*1 NAXSI_FMT: ip=127.0.0.1&server=127.0.0.1&uri=/&learning=0&vers=0.50&total_processed=1&total_blocked=1&zone0=ARGS&id0=1302&var_name0=a, client: 127.0.0.1, server: , request: "GET /?a=a<>bcd HTTP/1.0", host: "127.0.0.1"
|
||||
```
|
||||
|
||||
### Naxsi Internal IDs
|
||||
|
||||
"User defined" rules are supposed to have IDs > `1000`.
|
||||
|
||||
IDs inferior `1000` are reserved for [[naxsi internal rules|embedded_rules]], which are usually related to protocol sanity and things that cannot be expressed through regular expressions or string matches.
|
||||
|
||||
Think twice before whitelisting one of those IDs, as it might partially/totally disable naxsi.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Classical benchmark : Web Vuln Scanner vs WAF
|
||||
|
||||
Here we will present the results of (IBM) [AppScan]( https://www-03.ibm.com/software/products/en/appscan/ ) vs naxsi.
|
||||
|
||||
Why AppScan ? Because, it's one of the less worse commercial scanner available, because they offer a target/demo (read : highly vulnerable) website, and, unlike [Acunetix]( https://www.acunetix.com/ ), you can run full tests on the target website.
|
||||
|
||||
# Overall Results
|
||||
|
||||
As you will see bellow, AppScan is not able to find any vulnerabilities that it can exploit. The only vulnerabilities detected are probed with extremely simple patterns (read : nearly full ascii) that are not (and will not) be blocked by naxsi (to avoid false positives).
|
||||
|
||||
## Raw results
|
||||
|
||||
When running AppScan against [demo.testfire.net]( http://demo.testfire.net ) with naxsi (set-up as forward proxy for this purpose), the following vulnerabilities will appear:
|
||||
|
||||

|
||||
|
||||
I won't make an exhaustive diff list between naxsi and raw results since, as you can expect, original scan results have a LOT of vulnerabilities, as this website is designed to show all the vulnerabilities AppScan can detect/exploit.
|
||||
|
||||
Let's go through all the "remaining" bugs :
|
||||
|
||||
## Blind SQL Injections
|
||||
|
||||
The scan result shows at least 2 Blind SQL Injection remaining. Actually, the attack pattern used by AppScan is the following :
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
The first test pattern `listAccounts=0%2B0%2B0%2B1001160141` is decoded to `listAccounts=0+0+0+1001160141`, and the seconde one, `before=1234+and+7659%3D7659` to `before=1234 and 7659=7659`. Appscan is able to detect the vulnerability, but it will not be able to exploit it.
|
||||
|
||||
## Database Error Pattern
|
||||
|
||||
During the scan, AppScan was able to detect several "Database Error Pattern" (read : MSSQL/MySQL/... error messages in webpage)
|
||||
|
||||

|
||||
|
||||
The test pattern used by AppScan is `100116014WFXSSProbe`, it's a generic one to *trace back* injected data into a webpage. Here, the scanner is able to trigger the bug by entering a non-numeric value in a numeric field, making the page yield a SQL error message. Obviously, you cannot do anything here except blocking the scanner's pattern, which would be dumb and inefficient. Obviously, the injected characters won't allow any kind of exploitation of the vulnerability, only a probe.
|
||||
|
||||
## Other vulnerabilities
|
||||
|
||||
The other vulnerabilities reported by AppScan :
|
||||
|
||||
* Weak credentials : Because it's possible to login with admin/admin. No comment Out of scope for a WAF.
|
||||
* Session Reuse : When logging / logging off and logging in again, AppScan notices that the Cookie (session) doesn't change. (Out of scope for a WAF)
|
||||
* Weak Cookies : Sensitive information is stored in the user's cookie, enabling cookie theft if someone can access the user's workstation. (Out of scope for a WAF)
|
||||
* No account lock : An attacker can brute-force passwords. (Out of scope for a WAF, but nginx with req_limit might help you solving that !)
|
||||
* CSRF : This is more touchy, because it can be blocked by NAXSI, by adding a rule like `BasicRule "rx:http://demo.testfire.net/*" "mz:$HEADERS:Referer";`, but we didn't add it, because we wanted this scan to be really "out of the box" with no customization.
|
||||
@@ -0,0 +1,193 @@
|
||||
### Introduction/Disclaimer
|
||||
|
||||
First of all, and to prevent any trolls : We're **not** saying that naxsi is better than [mod_security]( https://modsecurity.org/ ). We think that mod_security is a great project and we have a huge respect for its team.
|
||||
|
||||
That being said, we will present you the results of the mod_security SQLi challenge. This challenge was organized by the mod_security people, and participants had to bypass mod_security to perform SQLi on various vulnerable web sites.
|
||||
|
||||
As you will see, none of the attack patterns used will bypass naxsi, because naxsi isn't clever and doesn't try to interpret stuff in requests, avoiding the risk of getting bypassed by obfuscated syntax.
|
||||
|
||||
Last but not least, the more uncommon a request is, the more chance they have to be blocked by naxsi.
|
||||
|
||||
Detailed information about the mod_security challenge can be found [here]( http://blog.spiderlabs.com/2011/07/modsecurity-sql-injection-challenge-lessons-learned.html )
|
||||
|
||||
#### Bypass #1 : Johannes Dahse
|
||||
|
||||
##### Payload
|
||||
```
|
||||
http://www.modsecurity.org/testphp.vulnweb.com/artists.php?artist=0+div+1+union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A1%2C2%2Ccurrent_user
|
||||
```
|
||||
|
||||
##### Decoded
|
||||
|
||||
```
|
||||
http://www.modsecurity.org/testphp.vulnweb.com/artists.php?artist=0+div+1+union#foo*/*bar
|
||||
select#foo
|
||||
1,2,current_user
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
|
||||
* Multiple SQL keywords
|
||||
* SQL comments
|
||||
* Uncommon characters : `, #*`
|
||||
|
||||
#### Bypass #2 : Vladimir Vorontsov
|
||||
|
||||
##### Payload
|
||||
|
||||
```
|
||||
FromDate=a1%27+or&ToDate=%3C%3Eamount+and%27
|
||||
```
|
||||
|
||||
##### Decoded
|
||||
|
||||
```
|
||||
FromDate=a1'+or&ToDate=<>amount+and'
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
* Multiple quotes
|
||||
* `<>` Signs
|
||||
|
||||
#### Bypass #3 : PT Research
|
||||
|
||||
##### Payload
|
||||
|
||||
```
|
||||
after=1 AND (select DCount(last(username)&after=1&after=1) from users where username='ad1min')&before=d
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
* Multiple SQL keywords
|
||||
* Multiple parenthesis
|
||||
* Multiple quotes
|
||||
* Naxsi is immune to splitting attacks
|
||||
|
||||
#### Bypass #4 : Ahmad Maulana
|
||||
|
||||
##### Payload
|
||||
|
||||
```
|
||||
ToDate=1'UNION/*!0SELECT user,2,3,4,5,6,7,8,9/*!0from/*!0mysql.user/*-
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
* Multiple SQL keywords
|
||||
* SQL comments
|
||||
* Multiple quotes
|
||||
* Uncommon characters : `, !`
|
||||
|
||||
#### Bypass #5 : Travis Lee
|
||||
|
||||
##### Payload
|
||||
|
||||
```
|
||||
Cookie: ASP.NET_SessionId=c0tx0o455d0b10ylsdr03m55; amSessionId=14408158863; amUserInfo=UserName=YWRtaW4=&
|
||||
Password=JyBvciAnMSc9JzEnLS0=; amUserId=1 union select username,password,3,4 from users
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
* Multiple SQL keywords
|
||||
* Multiple commas
|
||||
* This one is really interesting, as mod_security was bypassed, not because of the pattern, but because of its location (Cookie Headers)
|
||||
|
||||
#### Bypass #6 : Roberto Salgado
|
||||
|
||||
##### Payload
|
||||
|
||||
```
|
||||
http://www.modsecurity.org/testphp.vulnweb.com/artists.php?artist=-
|
||||
2%20div%201%20union%20all%23yeaah%0A%23yeah%20babc%0A%23fdsafdsafa%23fafsfaf%23%23yea%0A
|
||||
%23yeah%20babc%0A%23fdsafdsafa%23fafsfaf%23%23yeaah%0A%23yeah%20babc%0A%23fdsafdsafa%23f
|
||||
afsfaf%23%23yea%0A%23yeah%20babc%0A%23fdsafdsafa%23fafsfaf%23%23yeaah%0A%23yeah%20babc%0
|
||||
A%23fdsafdsafa%23fafsfaf%23%23yea...
|
||||
truncated
|
||||
...3fafsfaaf%23fafsfaaf%23fafsfaaf%23fafsfaaf%
|
||||
0Aselect%200x00,%200x41%20like/*!31337table_name*/,3%20from%20information_schema.tables%
|
||||
20limit%201
|
||||
```
|
||||
|
||||
##### Decoded
|
||||
|
||||
```
|
||||
2 div 1 union all#yeaah
|
||||
#yeah babc
|
||||
#fdsafdsafa#fafsfaf##yea
|
||||
#yeah babc
|
||||
#fdsafdsafa#fafsfaf##yeaah...
|
||||
truncated
|
||||
...
|
||||
like/*!31337table_name*/,3 from information_schema.tables...
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
* Multiple SQL keywords
|
||||
* SQL comments
|
||||
* uncommon characters : `# , !`
|
||||
|
||||
#### Bypass #7 : Sqlmap Developers
|
||||
|
||||
##### Payload
|
||||
|
||||
```
|
||||
http://www.modsecurity.org/testphp.vulnweb.com/artists.php?artist=%40%40new%20union%23sqlmapsqlmap...%0Aselect%201,2,database%23sqlmap%0A%28%29
|
||||
```
|
||||
|
||||
##### Decoded
|
||||
|
||||
```
|
||||
http://www.modsecurity.org/testphp.vulnweb.com/artists.php?artist=@@new union#sqlmapsqlmap...
|
||||
select 1,2,database#sqlmap
|
||||
()
|
||||
```
|
||||
|
||||
###### Explanation
|
||||
* Multiple SQL keywords
|
||||
* Uncommon characters : `.. # , ()`
|
||||
|
||||
#### Bypass #8 : HackPlayers
|
||||
|
||||
###### Payload
|
||||
|
||||
```
|
||||
http://www.modsecurity.org/testphp.vulnweb.com/artists.php?artist=-
|
||||
2%20div%201%20union%20all%23hack%0A%23hpys%20player%0A%23fabuloso%23great%0A%23hpys%20pl
|
||||
ayer%0A%23fabuloso%23modsec%0A%23hpys%20player%0A%23fabuloso%23great%0A%23hpys%20player%
|
||||
0A%23fabuloso%23modsec%0A%23h...
|
||||
truncated
|
||||
...23hpys%20player%0A%23fabuloso%23great%23%0A%23fabuloso%23great%23%0Aselect%200x00%2C%200
|
||||
x41%20not%20like%2F*%2100000table_name*%2F%2C3%20from%20information_schema.tables%20limi
|
||||
t%201
|
||||
```
|
||||
|
||||
##### Decoded
|
||||
|
||||
```
|
||||
-2 div 1 union all#hack
|
||||
#hpys player
|
||||
#fabuloso#great...
|
||||
truncated
|
||||
...
|
||||
select 0x00, 0x41 not like/*!00000table_name*/,3 from information_schema.tables limit 1
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
|
||||
* Multiple SQL keywords
|
||||
* SQL comments
|
||||
* Uncommon characters : `# , ! `
|
||||
|
||||
#### Bypass #9 : Georgi Geshev
|
||||
|
||||
##### Payload
|
||||
|
||||
```
|
||||
http://www.modsecurity.org/testphp.vulnweb.com/listproducts.php?artist=1%0bAND(SELECT%0b1%20FROM%20mysql.x)
|
||||
```
|
||||
|
||||
##### Explanation
|
||||
* Multiple SQL keywords
|
||||
|
||||
|
||||
### Final WORD
|
||||
All those tests where realized on a 'out of the box' naxsi, we I did no customization nor any modifications whatsoever.
|
||||
@@ -0,0 +1,127 @@
|
||||
Naxsi is using a simple key-value system for its rules. For example `MainRule "msg:this is a message" "str:searchstring" "mz:URL|BODY|ARGS" "s:$XSS:8" id:12345678990;` is a *main* rule with the *id* 12345678990, that search the string `searchstring` in the *url*, the *body* and the *arguments* of the request, and upon match, it will increase the `XSS` score by 8.
|
||||
|
||||
Everything must be quoted with double quotes, except the id part.
|
||||
|
||||
```
|
||||
|
||||
MainRule "msg:this is a message" "str:searchstring" "mz:URL|BODY" "s:$XSS:8" id:12345;
|
||||
| | | | | +-> UNIQUE ID
|
||||
| | | | |
|
||||
| | | | +-> SCORE
|
||||
| | | |
|
||||
| | | +-> MZ
|
||||
| | |
|
||||
| | +-> SearchString/RegEx
|
||||
| |
|
||||
| +-> MESSAGE
|
||||
|
|
||||
+-> RuleDesignator
|
||||
```
|
||||
|
||||
## Designator
|
||||
Either `MainRule` or `BasicRule`.
|
||||
|
||||
BasicRule and MainRule can be used either for rules or whitelist.
|
||||
The difference is the scope : BasicRule is valid in location {} context, while MainRule is valid at http {} context.
|
||||
|
||||
## Match Pattern (str:... rx:...)
|
||||
|
||||
**Match pattern** can be a regular expression or a string match:
|
||||
|
||||
* `rx:foo|bar` : will match `foo` or `bar`
|
||||
* `str:foo|bar` : will match `foo|bar`
|
||||
* `d:libinj_xss` : will match if libinjection says it's XSS (**>= 0.55rc2**)
|
||||
* `d:libinj_sql` : will match if libinjection says it's SQLi (**>= 0.55rc2**)
|
||||
|
||||
Using plain string match when possible is recommended, as it's way faster.
|
||||
All strings *must* be lowercase, since naxsi's matches are case insensitive.
|
||||
|
||||
## Human readable message (msg:...)
|
||||
|
||||
**msg** is a string describing the pattern. This is mostly used for analyzing and to have some human-understandable text.
|
||||
|
||||
|
||||
## Score section (s:...)
|
||||
|
||||
**s** is the score section. You can create "named" counters: `s:$FOOBAR:4` will increase counter `$FOOBAR` value by 4. One rule can increase several scores: `s:$FOO:4,$BAR:8` will increase both `$FOO` by `4` and `$BAR` by `8`. A rule can as well directly specifiy an action such a BLOCK (blocks the request in non-learning mode) or DROP (blocks the request **even** in learning mode)
|
||||
|
||||
## Match Zone (mz:...)
|
||||
|
||||
**mz** is the match zone, defining which part of the request will be inspected by the rule (or the whitelist, it works in the same way).
|
||||
|
||||
`mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie` means that pattern will be searched in the whole *body* (content and name of each var), as well as in **url** (before the '?') and in the get **arguments** (content and name of each var).
|
||||
|
||||
`$HEADERS_VAR:Cookie` means that the pattern will be as well applied to the HTTP header named `Cookie`. Filenames of file uploads can be check as well with the `FILE_EXT` zone.
|
||||
|
||||
See [Whitelists generation]( whitelists ), as same `mz:` usage applies. ie. `mz:$URL:/foo|$ARGS_VAR:arg42` will create a rule that will only be looked against GET argument `arg42` of request to URL `/foo`. (**fixed in >= 0.55rc2**)
|
||||
|
||||
You can as well use the `_X` (>= 0.52) equivalents, that allows you to create matchzone matching regular expressions, for example: `mz:$URL_X:^/foobar[0-9]+$` to match URLs such as `foobar1`, `foobar000`, etc. However, because of a known limitation, you can't use `|` character in your `_X` regular expressions, because it is used as a field separator by naxsi rule parsing.
|
||||
|
||||
Starting from naxsi **0.55rc0**, for unknown content-types, you can use the `RAW_BODY` match-zone. `RAW_BODY` rules looks like that:
|
||||
|
||||
```
|
||||
MainRule id:4241 s:DROP str:RANDOMTHINGS mz:RAW_BODY;
|
||||
```
|
||||
|
||||
Rules in the `RAW_BODY` zone will only applied when:
|
||||
- The *Content-type* is unknown (which means naxsi doesn't know how to properly parse the request)
|
||||
- `id 11` (which is the internal blocking rule for 'unknown content-type') is whitelisted.
|
||||
|
||||
Then, the full body (url decoded and with null-bytes replaced by '0') is passed to this set of rules.
|
||||
The full body is matched again the regexes or string matches.
|
||||
|
||||
Whitelists for `RAW_BODY` rules are actually written just like normal body rules, such as:
|
||||
|
||||
```
|
||||
BasicRule wl:4241 "mz:$URL:/rata|BODY";
|
||||
```
|
||||
|
||||
### Valid *mz*
|
||||
|
||||
- **URL** Full URI (server-path of a request)
|
||||
- **ARGS** Request-Arguments (all the things behind the `?` character in a GET-Request)
|
||||
- **BODY** Request-Data from a POST-Request
|
||||
- **HEADERS**
|
||||
- **$HEADERS_VAR:[value]** any HTTP-HEADERS-var that is available, eg
|
||||
- **$HEADERS_VAR:User-Agent**
|
||||
- **$HEADERS_VAR:Cookie**
|
||||
- **$HEADERS_VAR:Content-Type**
|
||||
- **$HEADERS_VAR:Connection**
|
||||
- **$HEADERS_VAR:Accept-Encoding**
|
||||
- **FILE_EXT** Filename (in a multipart POST containing a file)
|
||||
|
||||
For rules, each item of the match zone is considered inclusive **OR** (with the exception of $URL / $URL_X that **must** be satisfied if present in match-zone).
|
||||
|
||||
## ID (id:...)
|
||||
|
||||
**id** is the ID of the rule, that will be used in `NAXSI_FMT` or to whitelist it.
|
||||
|
||||
IDs inferior to `1000` are reserved for naxsi internal rules (protocol mismatch etc.)
|
||||
|
||||
## Negative Keyword (negative)
|
||||
|
||||
**negative** is a keyword that can be used to make a negative rule.
|
||||
Score is applied when the rule doesn't match :
|
||||
|
||||
```
|
||||
MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402;
|
||||
```
|
||||
|
||||
# Examples
|
||||
|
||||
This will look for the string `Submit=Run` on the url `/script`, with the *POST* variable `Submit` present:
|
||||
```
|
||||
MainRule "msg:detection Submit=Run in POST" "str:Submit=Run" "mz:$URL:/script|$BODY_VAR:Submit" "s:$ATTACK" id: 1230001;
|
||||
```
|
||||
|
||||
This will look for accesses on the `/hidden.html` url:
|
||||
```
|
||||
MainRule "msg:detection URL-Access" "str:/hidden.html" "mz:URL" "s:$ATTACK" id:1230002;
|
||||
```
|
||||
|
||||
This will detect the string `jjoplmh` in the `cms` *GET* variable:
|
||||
```
|
||||
MainRule "str:jjoplmh" "msg:Possible Wordpress-Plugin-Backdoor detected" "mz:$ARGS_VAR:cms" "s:$UWA:8" id:42000347;
|
||||
```
|
||||
|
||||
naxsi_core.rules contains examples of rules. See as well mex's [[Doxi rules | https://bitbucket.org/lazy_dogtown/doxi-rules]] for more rules examples (doxi is third party rules that will focus on emerging threats).
|
||||
@@ -0,0 +1,21 @@
|
||||
# Unit Tests
|
||||
|
||||
Naxsi comes with [unit tests]( https://github.com/nbs-system/naxsi/tree/master/t ).
|
||||
This is very useful for people who wants to modify naxsi, build packages, test it on exotic setups, …
|
||||
We try to keep our [code coverage]( http://codecov.io/github/nbs-system/naxsi?branch=master ) as high as possible.
|
||||
|
||||
This is how you can run them:
|
||||
|
||||
1. Untar [nginx code source]( http://nginx.org/en/download.html ) in `/tmp/nginx/`.
|
||||
2. Setup naxsi for unit testing (from [naxsi_src]( https://github.com/nbs-system/naxsi/tree/master/naxsi_src )).
|
||||
* `make re` will compile naxsi to run from `/tmp/`
|
||||
* `make deploy ` will deploy a minimal naxsi in `/tmp/naxsi_ut/` (running on high port)
|
||||
* `make test` will run the unit tests
|
||||
3. It will give you a pass/fail result.
|
||||
4. Look at the resulting `/tmp/naxsicov.html/` to get a code coverage report.
|
||||
|
||||
If you're too lazy to run them, no worries, [travis-ci]( https://travis-ci.org/nbs-system/naxsi ) is running them for every pull-request that you'll submit!
|
||||
|
||||
## Coverity
|
||||
|
||||
[Coverity]( https://scan.coverity.com/projects/1883 ) is cool enough to allow open-source projects to use their cloud solutions for free. We do submit builds for every release. If you want to have access to detail results (ie. doing security assessment on naxsi), get in touch with us!
|
||||
@@ -0,0 +1,103 @@
|
||||
### BasicRule
|
||||
BasicRule(s) are present at the location's configuration level. It is (most of the time) used to create whitelists. BasicRule syntax is :
|
||||
|
||||

|
||||
|
||||
#### **wl:ID** (WhiteList)
|
||||
|
||||
Which rule ID(s) are whitelisted. Possible syntax are:
|
||||
|
||||

|
||||
|
||||
* `wl:0` : Whitelist all rules
|
||||
* `wl:42` : Whitelist rule `#42`
|
||||
* `wl:42,41,43` : Whitelist rules `42`, `41` and `43`
|
||||
* `wl:-42` : Whitelist all user rules (`>= 1000`), excepting rule `42`
|
||||
|
||||
|
||||
|
||||
#### **mz:** (MatchZones)
|
||||
|
||||

|
||||
|
||||
Specify the zones (see below) in which the exception is allowed. Existing Zones are the following :
|
||||
|
||||
- `ARGS`: GET args
|
||||
- `$ARGS_VAR `: named GET argument
|
||||
- `$ARGS_VAR_X`: regex matching the name of a GET argument
|
||||
- `HEADERS `: HTTP Headers
|
||||
- `$HEADERS_VAR ` : named HTTP header
|
||||
- `$HEADERS_VAR_X`: regex matching a named HTTP header
|
||||
- `BODY `: POST args (and RAW_BODY)
|
||||
- `$BODY_VAR `: named POST argument
|
||||
- `$BODY_VAR_X`: regex matching the name of a POST argument
|
||||
- `URL`: The URL (before '?')
|
||||
- `$URL`: The specified URL
|
||||
- `$URL_X`: regex matching the URL (before '?')
|
||||
- `FILE_EXT`: Filename (in a multipart POST containing a file)
|
||||
|
||||
### Whitelist Example
|
||||
|
||||
Totally disable rule #1000 for this location, matchzone is empty, so the whitelist always matches.
|
||||
|
||||
```
|
||||
BasicRule wl:1000;
|
||||
```
|
||||
|
||||
Disable rule #1000 on all url in GET argument named `foo`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR:foo";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in GET argument named `foo` for url `/bar`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR:foo|$URL:/bar";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments for url `/bar`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$URL:/bar|ARGS";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET argument NAMES (only name, not content):
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:ARGS|NAME";
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- A zone can be suffixed with `|NAME`, meaning the rule matched in the name of the variable, but not its content.
|
||||
- Both matchzone content and patterns (`rx:`/`str:`) must be in lower-case, as naxsi is case insensitive
|
||||
- `RAW_BODY` whitelists are written just as any `BODY` whitelist, see [[rulessyntax]]
|
||||
- A whitelist can't mix `_X` elements with `_VAR` or `$URL` items. ie:
|
||||
|
||||
```
|
||||
$URL_X:/foo|$ARGS_VAR:bar : WRONG
|
||||
$URL_X:^/foo$|$ARGS_VAR_X:^bar$ : GOOD
|
||||
```
|
||||
|
||||
### Regex Whitelist Examples
|
||||
|
||||
Available only in [naxsi 0.52]( https://github.com/nbs-system/naxsi/releases/tag/0.52 ) and later.
|
||||
|
||||
Disable rule `#1000` in all GET arguments containing `meh`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:meh";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in GET argument named `meh`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:^meh";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments matching `meh_<number>`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:^meh_[0-9]+$"
|
||||
```
|
||||
@@ -0,0 +1,99 @@
|
||||
### BasicRule
|
||||
BasicRule(s) are present at the location's configuration level. It is (most of the time) used to create whitelists. BasicRule syntax is :
|
||||
|
||||
```
|
||||
BasicRule wl:ID [mz:[$URL:target_url]|[match_zone]|[$ARGS_VAR:varname]|[$BODY_VAR:varname]|[$HEADERS_VAR:varname]|[NAME]]
|
||||
```
|
||||
|
||||
#### **wl:ID** (WhiteList)
|
||||
|
||||
Which rule ID(s) are whitelisted. Possible syntax are:
|
||||
|
||||
* `wl:0` : Whitelist all rules
|
||||
* `wl:42` : Whitelist rule `#42`
|
||||
* `wl:42,41,43` : Whitelist rules `42`, `41` and `43`
|
||||
* `wl:-42` : Whitelist all user rules (`>= 1000`), excepting rule `42`
|
||||
|
||||
#### **mz:** (MatchZones)
|
||||
|
||||
Specify the zones (see below) in which the exception is allowed. Existing Zones are the following :
|
||||
|
||||
- `ARGS`: GET args
|
||||
- `$ARGS_VAR `: named GET argument
|
||||
- `$ARGS_VAR_X`: regex matching the name of a GET argument
|
||||
- `HEADERS `: HTTP Headers
|
||||
- `$HEADERS_VAR ` : named HTTP header
|
||||
- `$HEADERS_VAR_X`: regex matching a named HTTP header
|
||||
- `BODY `: POST args (and RAW_BODY)
|
||||
- `$BODY_VAR `: named POST argument
|
||||
- `$BODY_VAR_X`: regex matching the name of a POST argument
|
||||
- `URL`: The URL (before '?')
|
||||
- `$URL`: The specified URL
|
||||
- `$URL_X`: regex matching the URL (before '?')
|
||||
- `FILE_EXT`: Filename (in a multipart POST containing a file)
|
||||
|
||||
### Whitelist Example
|
||||
|
||||
Totally disable rule #1000 for this location, matchzone is empty, so the whitelist always matches.
|
||||
|
||||
```
|
||||
BasicRule wl:1000;
|
||||
```
|
||||
|
||||
Disable rule #1000 on all url in GET argument named `foo`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR:foo";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in GET argument named `foo` for url `/bar`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR:foo|$URL:/bar";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments for url `/bar`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$URL:/bar|ARGS";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET argument NAMES (only name, not content):
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:ARGS|NAME";
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- A zone can be suffixed with `|NAME`, meaning the rule matched in the name of the variable, but not its content.
|
||||
- Both matchzone content and patterns (`rx:`/`str:`) must be in lower-case, as naxsi is case insensitive
|
||||
- `RAW_BODY` whitelists are written just as any `BODY` whitelist, see [[rulessyntax]]
|
||||
- A whitelist can't mix `_X` elements with `_VAR` or `$URL` items. ie:
|
||||
|
||||
```
|
||||
$URL_X:/foo|$ARGS_VAR:bar : WRONG
|
||||
$URL_X:^/foo$|$ARGS_VAR_X:^bar$ : GOOD
|
||||
```
|
||||
|
||||
### Regex Whitelist Examples
|
||||
|
||||
Available only in [naxsi 0.52]( https://github.com/nbs-system/naxsi/releases/tag/0.52 ) and later.
|
||||
|
||||
Disable rule `#1000` in all GET arguments containing `meh`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:meh";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in GET argument named `meh`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:^meh";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments matching `meh_<number>`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:^meh_[0-9]+$"
|
||||
```
|
||||
@@ -0,0 +1,79 @@
|
||||
# Libinjection integration
|
||||
|
||||
[libinjection](https://github.com/client9/libinjection) is a 3rd party library (developped by client9) aiming at detecting SQL injections (sqli) and cross-site scripting (xss) by tokenization. This library is integrated within naxsi for two purposes :
|
||||
- generic detection of xss/sqli
|
||||
- virtual patching
|
||||
|
||||
### Generic Detection
|
||||
|
||||
libinjection generic detection *must* be explicitely enabled using specific directives : [LibInjectionXss](directives.md#libinjectionxss) or [LibInjectionSql](directives.md#libinjectionsql). It can as well be enabled at runtime using [runtime modifiers](runtime-modifiers.md) : `naxsi_flag_libinjection_xss` and `naxsi_flag_libinjection_sql`.
|
||||
|
||||
* Generic libinjection_xss rule has internal id 18 and increases named score `$LIBINJECTION_XSS` of 8 per match.
|
||||
|
||||
* Generic libinjection_sql rule has internal id 17 and increases named score `$LIBINJECTION_SQL` of 8 per match.
|
||||
|
||||
|
||||
A generic setup to block any request triggering libinjection_xss looks like :
|
||||
|
||||
```
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
LibInjectionXss;
|
||||
CheckRule "$LIBINJECTION_XSS >= 8" BLOCK;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
for libinjection_sql :
|
||||
```
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
LibInjectionSql;
|
||||
CheckRule "$LIBINJECTION_SQL >= 8" BLOCK;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
When generic detection is enabled, false positives can be whitelisted using id 17 ([libinjection_xss](internal-rules.md#libinjection_xss)) or 18 ([libinjection_sql](internal-rules.md#libinjection_sql)).
|
||||
|
||||
|
||||
Using runtime modifiers, it might look like :
|
||||
|
||||
|
||||
```
|
||||
#/foobar as LOTS of sql injections
|
||||
if ($request_uri ~ ^/foobar(.*)$ ) {
|
||||
set $naxsi_flag_libinjection_sql 1;
|
||||
}
|
||||
...
|
||||
location / {
|
||||
...
|
||||
CheckRule "$LIBINJECTION_SQL >= 8" DROP;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Virtual Patching
|
||||
|
||||
(>= 0.55rc2)
|
||||
|
||||
Widely enabling libinjection might not be possible depending on the application context.
|
||||
However, libinjection can as well be used for virtual patching :
|
||||
|
||||
```
|
||||
MainRule "d:libinj_xss" "s:DROP" "mz:$ARGS_VAR:ruuu" id:41231;
|
||||
```
|
||||
|
||||
|
||||
_Pass the content of GET variable 'ruuu' to libinjection, and drop request if it's detected as xss_
|
||||
|
||||
```
|
||||
MainRule "d:libinj_sql" "s:DROP" "mz:$ARGS_VAR:ruuu" id:41231;
|
||||
```
|
||||
|
||||
_DROP any request triggering libinjection_sql in GET variable 'ruuu'_
|
||||
|
||||
|
||||
Using virtual patching approach, user-created rules can be managed/whitelisted without specificities.
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
## Match Zones
|
||||
|
||||
Match Zones *mz* are present in rules and whitelists. It is used to specify where a pattern should be searched (rules) or where it should be allowed (whitelist).
|
||||
Please note that matchzones behave a bit differently in rules and whitelists : In rules each condition is *OR* (ie. in `BODY` _or_ in `HEADERS`),
|
||||
while in whitelist it's *AND* (ie. url must be `/foo` _and_ exception must happen in `ARGS`)
|
||||
|
||||
### Global Zones
|
||||
|
||||
4 main zones exist : URL, ARGS, HEADERS, BODY, and matchzone can be more or less restrictive.
|
||||
|
||||
A mz can be wide :
|
||||
|
||||
- `ARGS`: GET args
|
||||
- `HEADERS `: HTTP Headers
|
||||
- `BODY `: POST args (and RAW_BODY)
|
||||
- `URL`: The URL itself (before '?')
|
||||
- `ALL`: TODO DOCUMENTATION
|
||||
|
||||
Or more specific :
|
||||
|
||||
- `$ARGS_VAR:string`: named GET argument
|
||||
- `$HEADERS_VAR:string` : named HTTP header
|
||||
- `$BODY_VAR:string`: named POST argument
|
||||
|
||||
Sometime, regular expressions are needed (ie. variable names can vary) :
|
||||
|
||||
- `$HEADERS_VAR_X:regex`: regex matching a named HTTP header (>= 0.52)
|
||||
- `$ARGS_VAR_X:regex`: regex matching the name of a GET argument (>= 0.52)
|
||||
- `$BODY_VAR_X:regex`: regex matching the name of a POST argument (>= 0.52)
|
||||
|
||||
A matchzone can be restricted to a specific URL :
|
||||
(_but is not a zone on its own_)
|
||||
|
||||
- `$URL:string`: restricted to this url
|
||||
- `$URL_X:regex`: restricted to url matching regex (>= 0.52)
|
||||
|
||||
|
||||
A matchzone that targets BODY,HEADERS,ARGS can add `|NAME` to *specify* the target is not
|
||||
the content of a variable, but its name itself.
|
||||
|
||||
It is useful in specific contexts (ie. whitelist `[ ]` in form var names on url /foo)
|
||||
|
||||
`BasicRule id:1310,1311 "mz:$URL:/foo|BODY|NAME";`
|
||||
|
||||
|
||||
more specific, match-zones :
|
||||
- `FILE_EXT`: Filename (in a multipart POST containing a file)
|
||||
- `RAW_BODY`: A raw, unparsed representation of the BODY of a http request (>= 0.55rc0)
|
||||
|
||||
|
||||
|
||||
### Match Zone
|
||||
|
||||
A matchzone is a combination of one or several zone with an optional url.
|
||||
|
||||
In most situations, variable name and url can be predicted, and a static mz can be created :
|
||||
|
||||

|
||||
|
||||
When regular expressions are needed :
|
||||
|
||||

|
||||
|
||||
*note:* You CANNOT mix regex (`$URL_X`) and static (`$ARGS_VAR`) in a rule.
|
||||
|
||||
*$URL* and *$URL_X* are only used to restrict the scope of a matchzone, and are not specifying the zone.
|
||||
|
||||
### Whitelists matchzones
|
||||
|
||||
In whitelist context, *all* conditions must be satisfied :
|
||||
|
||||
`BasicRule wl:X "mz:$ARGS_VAR:foo|$URL:/bar";` \
|
||||
_id X is whitelisted in GET variable 'foo' on URL '/bar'_
|
||||
|
||||
### Rules matchzones
|
||||
|
||||
In rules context, `$URL` or `$URL_X` *must* be satisfied if present. Any other condition is treated as *OR* (opposite to whitelists).
|
||||
|
||||
`BasicRule str:Y id:X "mz:ARGS|BODY";`
|
||||
_pattern 'Y' will be matched against *any* GET and POST arguements_
|
||||
|
||||
`BasicRule str:Y id:X "mz:ARGS|BODY|$URL:/foo";`
|
||||
_pattern 'Y' will be matched against *any* GET and POST arguements as long as URL is /foo_
|
||||
|
||||
### Regex vs String
|
||||
|
||||
Matchzones composed of static (`$*_VAR:` `$URL:`) matchzones are stored in hashtables, and thus optimal.
|
||||
Regex matchzones (`$*_VAR_X:` `$URL_X:`) require more runtime processing.
|
||||
It is *not* possible to mix static and regex matchzone in a same rule/whitelist : `mz:$ARGS_VAR_X:^foo$|$URL:/x` or `mz:$URL_X:/foo|$ARGS_VAR:x` are wrong.
|
||||
@@ -0,0 +1,202 @@
|
||||
*Note: This process is intended to run on a fully up-to-date Debian Stretch, that follows [installation best practices](https://www.ssi.gouv.fr/guide/recommandations-de-securite-relatives-a-un-systeme-gnulinux/)*
|
||||
|
||||
### Build dependencies
|
||||
|
||||
- GCC
|
||||
- make
|
||||
- nginx :)
|
||||
- libpcre3-dev
|
||||
|
||||
### Get naxsi
|
||||
|
||||
You can download naxsi from the releases page [here](https://github.com/wargio/naxsi/releases)
|
||||
Get the latest release source code and the corresponding signature file :
|
||||
|
||||
```shell
|
||||
usr@008885ac189c:~$ export NAXSI_VER=1.3
|
||||
usr@008885ac189c:~$ wget https://github.com/wargio/naxsi/releases/download/$NAXSI_VER/naxsi-$NAXSI_VER-src-with-deps.tar.gz -O naxsi-$NAXSI_VER-src-with-deps.tar.gz
|
||||
```
|
||||
|
||||
### Get Nginx
|
||||
|
||||
Next, you need to get the source code for the nginx version you are running (in this example, we are using the nginx from the debian stretch repository) :
|
||||
|
||||
```shell
|
||||
usr@008885ac189c:~$ export NGINX_VER=X.YY.Z
|
||||
usr@008885ac189c:~$ wget https://nginx.org/download/nginx-$NGINX_VER.tar.gz
|
||||
usr@008885ac189c:~$ wget https://nginx.org/download/nginx-$NGINX_VER.tar.gz.asc
|
||||
usr@008885ac189c:~$ gpg --recv-key 520A9993A1C052F8
|
||||
usr@008885ac189c:~$ gpg --verify nginx-$NGINX_VER.tar.gz.asc
|
||||
usr@008885ac189c:~$ rm nginx-$NGINX_VER.tar.gz.asc
|
||||
```
|
||||
|
||||
If the signature fails, check that both files have been correctly downloaded and are not corrupted.
|
||||
|
||||
### Build naxsi as a dynamic extension
|
||||
|
||||
First, extract the source code :
|
||||
```shell
|
||||
usr@008885ac189c:~$ mkdir -p naxsi-$NAXSI_VER
|
||||
usr@008885ac189c:~$ tar -C naxsi-$NAXSI_VER -xzf naxsi-$NAXSI_VER-src-with-deps.tar.gz
|
||||
usr@008885ac189c:~$ tar vxf nginx-$NGINX_VER.tar.gz
|
||||
usr@008885ac189c:~$ cd nginx-$NGINX_VER
|
||||
usr@008885ac189c:~/nginx-X.YY.Z$ ./configure --add-dynamic-module=../naxsi-$NAXSI_VER/naxsi_src/
|
||||
usr@008885ac189c:~/nginx-X.YY.Z$ make modules
|
||||
```
|
||||
The resulting `ngx_http_naxsi_module.so` will be located in the `objs` directory.
|
||||
Copy it to your nginx server, in `/etc/nginx/modules/`.
|
||||
Also copy `naxsi_core.rules` to `/etc/nginx/`.
|
||||
|
||||
|
||||
### Build naxsi as a dynamic extension for nginx from your distribution package (i.e Ubuntu)
|
||||
|
||||
You need to build the module with the same flags as your NGINX server package was compiled for example via `apt-get source nginx`
|
||||
|
||||
Execute this to list them:
|
||||
```shell
|
||||
usr@008885ac189c:~$ nginx -V
|
||||
```
|
||||
Then pass them to `./configure`.
|
||||
|
||||
```shell
|
||||
usr@008885ac189c:~/nginx-X.YY.Z$ ./configure <FLAGS FROM ABOVE COMMAND> --add-dynamic-module=../naxsi-$NAXSI_VER/naxsi_src/
|
||||
usr@008885ac189c:~/nginx-X.YY.Z$ make modules
|
||||
```
|
||||
This will ensure module will be compatible with your nginx and will load properly.
|
||||
|
||||
|
||||
### Basic setup
|
||||
|
||||
#### Main configuration
|
||||
|
||||
You need to tell nginx to load the naxsi module and naxsi core rules (if you don't load them, naxsi will block every requests) :
|
||||
|
||||
```shell
|
||||
usr@008885ac189c:~$ cat /etc/nginx/nginx.conf
|
||||
...
|
||||
load_module /etc/nginx/modules/ngx_http_naxsi_module.so; # load naxsi
|
||||
|
||||
http {
|
||||
include /etc/nginx/naxsi_core.rules; # load naxsi core rules
|
||||
...
|
||||
}
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Naxsi works on a per-location basis, meaning you can only enable it inside a location :
|
||||
```python
|
||||
server {
|
||||
...
|
||||
|
||||
location / { # naxsi is enabled, and in learning mode
|
||||
|
||||
SecRulesEnabled; #enable naxsi
|
||||
LearningMode; #enable learning mode
|
||||
LibInjectionSql; #enable libinjection support for SQLI
|
||||
LibInjectionXss; #enable libinjection support for XSS
|
||||
|
||||
DeniedUrl "/RequestDenied"; #the location where naxsi will redirect the request when it is blocked
|
||||
CheckRule "$SQL >= 8" BLOCK; #the action to take when the $SQL score is superior or equal to 8
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 5" BLOCK;
|
||||
CheckRule "$UPLOAD >= 5" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
|
||||
|
||||
proxy_pass http://127.0.0.1;
|
||||
....
|
||||
}
|
||||
|
||||
location /admin { # naxsi is disabled
|
||||
|
||||
SecRulesDisabled; #optional, naxsi is disabled by default
|
||||
|
||||
allow 1.2.3.4;
|
||||
deny all;
|
||||
proxy_pass http://127.0.0.1;
|
||||
....
|
||||
}
|
||||
|
||||
location /vuln_page.php { # naxsi is enabled, and is *not* in learning mode
|
||||
|
||||
SecRulesEnabled;
|
||||
proxy_pass http://127.0.0.1;
|
||||
}
|
||||
|
||||
location /RequestDenied {
|
||||
internal;
|
||||
return 403;
|
||||
}
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### Whitelist
|
||||
|
||||
As naxsi uses a whitelist approach, a lot of false positives may be generated, potentially dropping legitimate requests.
|
||||
To prevent this, whitelists must be written (either manually or with [nx-tool](https://github.com/nbs-system/nxtool-ng)).
|
||||
For example, if you have an e-commerce website that sells furniture, people will be likely to search for something like `table`. Unfortunately, `table` is also a SQL keyword, which will trigger naxsi.
|
||||
To prevent this, you can write a whitelist telling naxsi to allow the `table` keyword in the search form (assuming the search form in on `/search`) :
|
||||
|
||||
```python
|
||||
server {
|
||||
|
||||
location / {
|
||||
SecRulesEnabled; #enable naxsi
|
||||
LearningMode; #enable learning mode
|
||||
LibInjectionSql; #enable libinjection support for SQLI
|
||||
LibInjectionXss; #enable libinjection support for XSS
|
||||
|
||||
DeniedUrl "/RequestDenied"; #the location where naxsi will redirect the request when it is blocked
|
||||
CheckRule "$SQL >= 8" BLOCK; #the action to take when the $SQL score is superior or equal to 8
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 5" BLOCK;
|
||||
CheckRule "$UPLOAD >= 5" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
|
||||
BasicRule wl:1000 "mz:$URL:/search|$ARGS_VAR:q";
|
||||
proxy_pass http://127.0.0.1;
|
||||
....
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
See [here](whitelists-bnf.md) and [here](whitelists-examples.md) for more informations about whitelists.
|
||||
|
||||
#### Blacklist
|
||||
|
||||
You can also create blacklist with naxsi, allowing you to drop requests even when in learning mode.
|
||||
Let's imagine a PHP script (`/vuln_page.php`) vulnerable to an unquoted SQLI in the `id` parameter, a typical exploitation would look like this :
|
||||
```
|
||||
GET /vuln_page.php?id=1'+or+1=1/*
|
||||
```
|
||||
You can use naxsi to virtually patch this vulnerabilty by adding a blacklist denying every requests on `/vuln_page.php` where the `id` parameter contains anything other than a number (in this example, the blacklist will only apply to the `/` location, you can use `MainRule` and put it outside in the `http` block to make it global):
|
||||
|
||||
```python
|
||||
server {
|
||||
|
||||
location / {
|
||||
SecRulesEnabled; #enable naxsi
|
||||
LearningMode; #enable learning mode
|
||||
LibInjectionSql; #enable libinjection support for SQLI
|
||||
LibInjectionXss; #enable libinjection support for XSS
|
||||
|
||||
DeniedUrl "/RequestDenied"; #the location where naxsi will redirect the request when it is blocked
|
||||
CheckRule "$SQL >= 8" BLOCK; #the action to take when the $SQL score is superior or equal to 8
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 5" BLOCK;
|
||||
CheckRule "$UPLOAD >= 5" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
|
||||
BasicRule id:4242 "mz:$URL:/vuln_page.php|$ARGS_VAR:id" "rx:[^\d]+" "s:DROP" "msg:blacklist for SQLI in /vuln_page.php";
|
||||
proxy_pass http://127.0.0.1;
|
||||
....
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,147 @@
|
||||
# Basic Setup
|
||||
|
||||
|
||||
### Architecture of naxsi configuration
|
||||
|
||||
* **http {}** level : `include naxsi_core.rules`
|
||||
* **server {}** level :
|
||||
* [Dynamic modifiers](runtime-modifiers.md)
|
||||
* **location {}** level :
|
||||
* [Enabled/Disabled directives](directives.md#secrulesenabled)
|
||||
* [LearningMode-related directives](directives.md#learningmode)
|
||||
* [Whitelists](whitelists-bnf.md)
|
||||
* [CheckRules](checkrules-bnf.md)
|
||||
* [RequestDenied](requestdenied-bnf.md)
|
||||
* **location /RequestDenied**
|
||||
* return HTTP error code, post-processing ...
|
||||
|
||||
|
||||
### Example configuration
|
||||
|
||||
```
|
||||
#Only for nginx's version with modular support
|
||||
load_module /../modules/ngx_http_naxsi_module.so;
|
||||
events {
|
||||
...
|
||||
}
|
||||
http {
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
...
|
||||
server {
|
||||
listen ...;
|
||||
server_name ...;
|
||||
location / {
|
||||
#Enable naxsi
|
||||
SecRulesEnabled;
|
||||
#Enable learning mode
|
||||
LearningMode;
|
||||
#Define where blocked requests go
|
||||
DeniedUrl "/50x.html";
|
||||
#CheckRules, determining when naxsi needs to take action
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$EVADE >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
#naxsi logs goes there
|
||||
error_log /.../foo.log;
|
||||
...
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
#This is where the blocked requests are going
|
||||
location = /50x.html {
|
||||
return 418; #I'm a teapot \o/
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Next steps
|
||||
|
||||
The next step is learning; however, before jumping there, ensure that you have:
|
||||
* A nginx as a webserver or reverse proxy
|
||||
* Naxsi installed and running in learning mode
|
||||
* If you perform a request such as `curl 'http://127.0.0.1:4242/?a=<>'`, you should see a [NAXSI_FMT](naxsilogs.md#naxsi_fmt) in your logs :
|
||||
`2016/07/12 13:27:04 [error] 14492#0: *1 NAXSI_FMT: ip=127.0.0.1&server=127.0.0.1&uri=/&learning=1&vers=0.55rc2&total_processed=1&total_blocked=1&block=1&cscore0=$XSS&score0=16&zone0=ARGS&id0=1302&var_name0=a&zone1=ARGS&id1=1303&var_name1=a, client: 127.0.0.1, server: localhost, request: "GET /?a=<> HTTP/1.1", host: "127.0.0.1:4242"`
|
||||
|
||||
|
||||
### Learning, log-injection, ElasticSearch, Kibana
|
||||
|
||||
The ElasticSearch/Kibana part is optional but provides a great comfort and way to visualize "what's going on".
|
||||
|
||||

|
||||
|
||||
|
||||
### Components
|
||||
|
||||
Nxtool setup can be found here:
|
||||
(_tl;dr: python setup.py install, or `./nxtool.py -c nxapi.json -x`_)
|
||||
* [nxapi documentation](https://github.com/nbs-system/naxsi/tree/master/nxapi#prequisites)
|
||||
|
||||
Kibana (v4 as of this writing) can be downloaded here:
|
||||
* [kibana website](https://www.elastic.co/downloads/kibana)
|
||||
|
||||
Once those two components are setup, you should be able to inject naxsi logs into ElasticSearch with NxTool.
|
||||
Here is an example of what it might look like in production:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Configuration - NXTOOL
|
||||
|
||||
As stated in dedicated documentation, nxtool comes with a `json` file specifying ES location, index etc.
|
||||
|
||||
```
|
||||
"elastic" : {
|
||||
"host" : "127.0.0.1:9200",
|
||||
"index" : "nxapi",
|
||||
"doctype" : "events",
|
||||
"default_ttl" : "7200",
|
||||
"max_size" : "1000",
|
||||
"version" : "2"
|
||||
},
|
||||
```
|
||||
|
||||
Once configured and running on the same host as nginx, you can start injecting logs into ES:
|
||||
|
||||
`nxtool.py --fifo /tmp/naxsi_pipe --no-timeout`
|
||||
_(here, nginx is being told to write logs to /tmp/naxsi_pipe which is a FIFO created by nxtool)_
|
||||
|
||||
### Configuration - Kibana
|
||||
|
||||
Now, you should be able to configure Kibana to setup a dashboard to visualize your naxsi data.
|
||||
|
||||
This step is left as an exercise for the reader, see
|
||||
[Creating Kibana Dashboard](https://www.elastic.co/guide/en/kibana/current/dashboard.html).
|
||||
|
||||
|
||||
|
||||
### Configuration - DataDog
|
||||
|
||||
Golden Setup
|
||||
|
||||
Blacklisting = mod_security
|
||||
|
||||
WAF = naxsi
|
||||
|
||||
https://gist.github.com/marcinguy/3a106991d3a84995efacc473f8db21a9
|
||||
|
||||
You can get logs to DataDog very easily using this Grok rule:
|
||||
|
||||
Grok Parser rule
|
||||
```
|
||||
myParsingrule %{date("yyyy/MM/dd HH:mm:ss"):connect_date} \[%{word:status}\] %{number:id}\#%{number:id2}: \*%{number:value} %{data::json}
|
||||
```
|
||||
|
||||
Then you will see a nice Dashboard of Attacks
|
||||
|
||||
[[https://user-images.githubusercontent.com/20355405/90318948-4c52a700-df34-11ea-8521-259551b5322e.png]]
|
||||
|
||||
You can also send alerts to Slack:
|
||||
|
||||
[[https://user-images.githubusercontent.com/20355405/92943641-52fd0d00-f453-11ea-99ee-411d9ea33105.png]]
|
||||
|
||||
You just built an Open Source WAF solution with Alerts and Dashboard.
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
### NAXSI_FMT
|
||||
|
||||
NAXSI_FMT are outputed by naxsi in your errorlog :
|
||||
|
||||
```
|
||||
2013/11/10 07:36:19 [error] 8278#0: *5932 NAXSI_FMT: ip=X.X.X.X&server=Y.Y.Y.Y&uri=/phpMyAdmin-2.8.2/scripts/setup.php&learning=0&vers=0.52&total_processed=472&total_blocked=204&block=0&cscore0=$UWA&score0=8&zone0=HEADERS&id0=42000227&var_name0=user-agent, client: X.X.X.X, server: blog.memze.ro, request: "GET /phpMyAdmin-2.8.2/scripts/setup.php HTTP/1.1", host: "X.X.X.X"
|
||||
```
|
||||
|
||||
Here, client `X.X.X.X` request to server `Y.Y.Y.Y` did trigger the rule `42000227` in the var named `user-agent` in the`HEADERS` zone. `id X` might seem obscure, but you can see the meaning in [naxsi_core.rules]( https://github.com/wargio/naxsi/blob/main/naxsi_rules/naxsi_core.rules ):
|
||||
|
||||
```
|
||||
MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302;
|
||||
```
|
||||
|
||||
NAXSI_FMT is composed of different items :
|
||||
|
||||
- `ip` : Client's ip
|
||||
- `server` : Requested Hostname (as seen in http header `Host`)
|
||||
- `uri`: Requested URI (without arguments, stops at `?`)
|
||||
- `learning`: tells if naxsi was in learning mode (0/1)
|
||||
- `vers` : Naxsi version, only since [0.51]( https://github.com/wargio/naxsi/tree/0.51 )
|
||||
- `total_processed`: Total number of requests processed by nginx's worker
|
||||
- `total_blocked`: Total number of requests blocked by (naxsi) nginx's worker
|
||||
- `zoneN`: Zone in which match happened (see "Zones" in the table below)
|
||||
- `idN`: The rule id that matched
|
||||
- `var_nameN`: Variable name in which match happened (optional)
|
||||
- `cscoreN` : _named_ score tag
|
||||
- `scoreN` : associated _named_ score value
|
||||
|
||||
Several groups of zone, id, var_name, cscore and score can be present in a single line.
|
||||
|
||||
### NAXSI_EXLOG
|
||||
|
||||
NAXSI_EXLOG is a complement to [[naxsilogs.md]]. Along with exceptions, it contains actual content of the matched request. While NAXSI_FMT only contains IDs and location of exception, NAXSI_EXLOG provides actual content, allowing you to easily decide if it's a false positive or not.
|
||||
|
||||
Learning tools uses this at his advantage. Extensive log is enabled by adding the following line in your server {} section but **out** of your location.
|
||||
|
||||
```javascript
|
||||
set $naxsi_extensive_log 1;
|
||||
```
|
||||
|
||||
This feature is provided by [[runtime-modifiers]].
|
||||
|
||||
```
|
||||
2013/05/30 20:47:05 [debug] 10804#0:*1 NAXSI_EXLOG: ip=127.0.0.1&server=127.0.0.1&uri=/&id=1302&zone=ARGS&var_name=a&content=a<>bcd
|
||||
2013/05/30 20:47:05 [error] 10804#0:*1 NAXSI_FMT: ip=127.0.0.1&server=127.0.0.1&uri=/&learning=0&vers=0.50&total_processed=1&total_blocked=1&zone0=ARGS&id0=1302&var_name0=a, client: 127.0.0.1, server: , request: "GET /?a=a<>bcd HTTP/1.0", host: "127.0.0.1"
|
||||
```
|
||||
|
||||
### Naxsi Internal IDs
|
||||
|
||||
"User defined" rules are supposed to have IDs > `1000`.
|
||||
|
||||
IDs inferior `1000` are reserved for [naxsi internal rules](internal-rules.md), which are usually related to protocol sanity and things that cannot be expressed through regular expressions or string matches.
|
||||
|
||||
Think twice before whitelisting one of those IDs, as it might partially/totally disable naxsi.
|
||||
|
||||
### Naxsi JSON Logs
|
||||
|
||||
TODO DOCUMENTATION
|
||||
|
||||
[directives.md#naxsi_json_log](directives)
|
||||
@@ -0,0 +1,39 @@
|
||||
# Raw body
|
||||
|
||||
RAW_BODY (>= 0.55rc0) is a feature to allow naxsi to match patterns in content it doesn't know to parse.
|
||||
|
||||
As stated in [internal rules](internal-rules.md), naxsi will bail out when it doesn't know content-type. If id:11 [bad content-type](internal-rules.md#uncommon_content_type) is whitelisted, then naxsi will go onto proceed all rules that are targeting `RAW_BODY`.
|
||||
|
||||
ie. configuration :
|
||||
|
||||
```
|
||||
http {
|
||||
...
|
||||
MainRule "id:4241" "s:DROP" "str:RANDOMTHINGS" "mz:RAW_BODY";
|
||||
...
|
||||
|
||||
location / {
|
||||
...
|
||||
BasicRule wl:11 "mz:$URL:/|BODY";
|
||||
...
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
ie. request :
|
||||
```
|
||||
POST / ...
|
||||
Content-Type: RAFARAFA
|
||||
...
|
||||
|
||||
|
||||
RANDOMTHINGS
|
||||
|
||||
```
|
||||
|
||||
Then rule 4241 will trigger. However, if `id:11` was not whitelisted, then rule 4241 wouldn't be proceed.
|
||||
|
||||
### Specifics
|
||||
|
||||
Before being passed on to raw_body parsing, all null bytes are replaced with actual 0's.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# DeniedUrl
|
||||
|
||||
DeniedUrl is used to indicate a location where blocked requests will be redirected (internally).
|
||||
|
||||
In version before 0.49, by default, naxsi forwards blocked request there while in learning mode. Upon "real" request termination, using nginx's post_action mechanism.
|
||||
|
||||
This was due to usage of nx_intercept, which could intercept learning traffic in live.
|
||||
|
||||
As the request might be modified during redirect (url & arguments), extra http headers orig_url (original url), orig_args (original GET args) and naxsi_sig (NAXSI_FMT) are added.
|
||||
|
||||
If $naxsi_flag_post_action is set to "1", naxsi will perform post_action (while in learning) even in versions '''> 0.49'''.
|
||||
|
||||
The headers that are forwarded to the location denied page are :
|
||||
* Naxsi orig url : x-orig_url header
|
||||
* Naxsi orig args : x-orig_args header
|
||||
* Naxsi orig sig : x-naxsi_sig header
|
||||
@@ -0,0 +1,90 @@
|
||||
# Rules
|
||||
|
||||
Rules are meant to search for patterns in parts of a request to detect attacks.
|
||||
|
||||
ie. DROP any request containing the string 'zz' in *any* GET or POST argument :
|
||||
`MainRule id:424242 "str:zz" "mz:ARGS|BODY" "s:DROP";`
|
||||
|
||||
Rules can be present at `location` level (`BasicRule`) or at `http` level (`MainRule`).
|
||||
|
||||
Rules have the following schema :
|
||||
|
||||

|
||||
|
||||
Everything must be quoted with double quotes, except the id part.
|
||||
|
||||
### ID (id:...)
|
||||
|
||||
`id:num` is the *unique* numerical ID of the rule, that will be used in `NAXSI_FMT` or whitelists.
|
||||
|
||||
IDs inferior to `1000` are reserved for naxsi internal rules (protocol mismatch etc.)
|
||||
|
||||
### Match Pattern
|
||||
|
||||

|
||||
|
||||
Match pattern can be a regular expression, a string match, or a call to a lib (libinjection) :
|
||||
|
||||
* `rx:foo|bar` : will match `foo` or `bar`
|
||||
* `str:foo|bar` : will match `foo|bar`
|
||||
* `d:libinj_xss` : will match if libinjection says it's XSS (**>= 0.55rc2**)
|
||||
* `d:libinj_sql` : will match if libinjection says it's SQLi (**>= 0.55rc2**)
|
||||
|
||||
Using plain string match when possible is recommended, as it's way faster.
|
||||
All strings *must* be lowercase, since naxsi's matches are case insensitive.
|
||||
|
||||
### Score (s:...)
|
||||
|
||||

|
||||
|
||||
**s** is the score section. You can create "named" counters: `s:$FOOBAR:4` will increase counter `$FOOBAR` value by 4. One rule can increase several scores: `s:$FOO:4,$BAR:8` will increase both `$FOO` by 4 and `$BAR` by 8.
|
||||
A rule can as well directly specifiy an action such a BLOCK (blocks the request in non-learning mode) or DROP (blocks the request **even** in learning mode)
|
||||
Named scores are later handled by [CheckRules](checkrules-bnf.md).
|
||||
|
||||
### MatchZone (mz:...)
|
||||
|
||||
Please refer to [Match Zones](matchzones-bnf.md) for details.
|
||||
|
||||
**mz** is the match zone, defining which part of the request will be inspected by the rule.
|
||||
|
||||
In rules, all matchzones but `$URL*:` are treated as *OR* conditions :
|
||||
|
||||
`MainRule id:4242 str:z "mz:$ARGS_VAR:X|BODY";`
|
||||
|
||||
pattern 'z' will be searched in GET var 'X' and *all* BODY vars.
|
||||
|
||||
`MainRule id:4242 str:z "mz:$ARGS_VAR:X|BODY|$URL_X:^/foo";`
|
||||
|
||||
pattern 'z' will be searched in GET var 'X' and all BODY vars *as long as* URL starts with `/foo`.
|
||||
|
||||
Starting from naxsi **0.55rc0**, for unknown content-types, you can use the `RAW_BODY` match-zone. `RAW_BODY` rules looks like that:
|
||||
|
||||
```
|
||||
MainRule id:4241 s:DROP str:RANDOMTHINGS mz:RAW_BODY;
|
||||
```
|
||||
|
||||
Rules in the `RAW_BODY` zone will only applied when:
|
||||
- The *Content-type* is unknown (which means naxsi doesn't know how to properly parse the request)
|
||||
- `id 11` (which is the internal blocking rule for 'unknown content-type') is whitelisted.
|
||||
|
||||
Then, the full body (url decoded and with null-bytes replaced by '0') is passed to this set of rules.
|
||||
The full body is matched again the regexes or string matches.
|
||||
|
||||
Whitelists for `RAW_BODY` rules are actually written just like normal body rules, such as:
|
||||
|
||||
```
|
||||
BasicRule wl:4241 "mz:$URL:/rata|BODY";
|
||||
```
|
||||
|
||||
### Human readable message (msg:...)
|
||||
|
||||
**msg** is a string describing the pattern. This is mostly used for analyzing and to have some human-understandable text.
|
||||
|
||||
### Negative Keyword (negative)
|
||||
|
||||
**negative** is a keyword that can be used to make a negative rule.
|
||||
Score is applied when the rule doesn't match :
|
||||
|
||||
```
|
||||
MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402;
|
||||
```
|
||||
@@ -0,0 +1,124 @@
|
||||
Go to [Rules Explanation](rules-bnf.md)
|
||||
|
||||
|
||||
* [generic rules](#generic-rules)
|
||||
* [Blocking "bad" user agents](#blocking-bad-user-agents)
|
||||
* [Blocking "bad" referers](#blocking-bad-referers)
|
||||
* [Blocking dangerous directories](#blocking-dangerous-directories)
|
||||
* [Virtual patching : Simple/Generic XSS](#simplegeneric-xss)
|
||||
* [Virtual patching : Simple/Generic (wider) XSS](#simplegeneric-wider-xss)
|
||||
* [Virtual patching : Simple/Generic File Upload](#simplegeneric-file-upload)
|
||||
* [Raw Body rules](#raw-body)
|
||||
* [LibInjection (XSS) Virtual Patching (>= 0.55rc1)](#libinjection-xss-virtual-patching)
|
||||
* [LibInjection (SQL) Virtual Patching (>= 0.55rc1)](#libinjection-sql-virtual-patching)
|
||||
* [Negative URL rule](#negative-rule)
|
||||
|
||||
|
||||
## 'generic' rules
|
||||
|
||||
Search for string `0x` in any POST/PUT arg, any part of the URL, any GET arg, or the HTTP header named `cookie` (extracted from naxsi_core.rules). If rule matches, `$SQL` score is increased by 2. Rule can be whitelisted via id `1002`.
|
||||
|
||||
```
|
||||
MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002;
|
||||
```
|
||||
|
||||
## Practices
|
||||
|
||||
Sometime, you can write rules to enforce best practices or simply to deter automated attacks.
|
||||
|
||||
|
||||
### Blocking "bad" user agents
|
||||
```
|
||||
MainRule "str:w3af.sourceforge.net" "msg:DN SCAN w3af User Agent" "mz:$HEADERS_VAR:User-Agent" "s:$UWA:8" id:42000041 ;
|
||||
```
|
||||
|
||||
Block w3af user-agent (http://w3af.org).
|
||||
|
||||
|
||||
### Blocking "bad" referers
|
||||
|
||||
```
|
||||
BasicRule "str:http://www.shadowysite.com/" "msg:Bad referer" "mz:$HEADERS_VAR:referer" "s:DROP" id:20001;
|
||||
```
|
||||
|
||||
|
||||
### Blocking dangerous directories
|
||||
|
||||
ie. following [CVE-2015-2067](http://cve.circl.lu/cve/CVE-2015-2067) on magento's plugin "magmi", you want to block access to the plugin :
|
||||
|
||||
```
|
||||
MainRule "str:/magmi/" "msg:Access to magmi folder" "mz:URL" "s:$UWA:8" id:42000400;
|
||||
MainRule "str:/magmi.php" "msg:Access to magmi.php" "mz:URL" "s:$UWA:8" id:42000401;
|
||||
```
|
||||
|
||||
## Vpatching Examples
|
||||
|
||||
Virtual patching usually aims at protecting a vulnerable software from exploitation.
|
||||
|
||||
### Simple/Generic XSS
|
||||
|
||||
There is a reflected XSS in GET variable "foo" on URL "/target" :
|
||||
|
||||
```
|
||||
MainRule id:4242 "str:<" "msg:xss (angle bracket)" "mz:$ARGS_VAR:foo|$URL:/target" s:DROP;
|
||||
```
|
||||
|
||||
This rule will stop any request containing the character '<' at the targeted location.
|
||||
|
||||
|
||||
### Simple/Generic (wider) XSS
|
||||
|
||||
There is a reflected XSS in GET variable "foo" on all product URLs :
|
||||
|
||||
```
|
||||
MainRule id:4242 "str:<" "msg:xss (angle bracket)" "mz:$ARGS_VAR_X:^foo$|$URL_X:^/product/[0-9]+/product$" s:DROP;
|
||||
```
|
||||
|
||||
|
||||
### Simple/Generic File Upload
|
||||
|
||||
Blocking asp/php file upload (part of core rules). Increases `$UPLOAD` by 8 if the string uploaded file names contains `ph` (.php / .pht ...) `.asp` or `.ht` (.htaccess ...).
|
||||
|
||||
```
|
||||
MainRule "rx:\.ph|\.asp|\.ht" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500;
|
||||
```
|
||||
|
||||
|
||||
### Raw Body
|
||||
|
||||
Raw Body zone is meant for the content-types that naxsi can't parse (XML, java serialized objects, unorthodox developments).
|
||||
See [RAW_BODY](rawbody.md) for details on RAW_BODY behaviour.
|
||||
|
||||
```
|
||||
MainRule "id:4241" "s:DROP" "str:RANDOMTHINGS" "mz:RAW_BODY";
|
||||
```
|
||||
|
||||
### LibInjection (XSS) Virtual Patching
|
||||
|
||||
(>= 0.55rc1)
|
||||
|
||||
Will drop any request for which libinjection detects content of GET var `foo` as an XSS.
|
||||
|
||||
```
|
||||
MainRule "id:4241" "s:DROP" "d:libinj_xss" "mz:$ARGS_VAR:foo";
|
||||
```
|
||||
|
||||
### LibInjection (SQL) Virtual Patching
|
||||
|
||||
(>= 0.55rc1)
|
||||
|
||||
Will drop any request for which libinjection detects content of GET var `foo` as an SQLi.
|
||||
|
||||
```
|
||||
MainRule "id:4241" "s:DROP" "d:libinj_sql" "mz:$ARGS_VAR:foo";
|
||||
```
|
||||
|
||||
### Negative rule
|
||||
|
||||
Will drop any request for which the URL doesn't start with "/rest/"
|
||||
|
||||
```
|
||||
MainRule "id:4241" negative "s:DROP" "rx:^/rest/" "mz:URL";
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
# naxsi dynamic configuration (aka nginx vars)
|
||||
|
||||
Since `0.49`, naxsi supports a limited set of variables that can override or modify its behavior.
|
||||
|
||||
* `naxsi_flag_learning` : If present, this variable will override naxsi learning flag ("0" to disable learning, "1" to enable it).
|
||||
* `naxsi_flag_post_action` : If present and set to "0" this variable may be used to disable post_action in learning mode.
|
||||
* `naxsi_flag_enable` : If present, this variable will override naxsi's "SecRulesEnabled" ("0" to disable naxsi, "1" to enable).
|
||||
* `naxsi_extensive_log` : If present (and set to "1"), this variable will force naxsi to log the CONTENT of variable matching rules (see notes at bottom).
|
||||
|
||||
Since `0.54`, naxsi as well support libinjection enable/disable flags at runtime
|
||||
* `naxsi_flag_libinjection_sql`
|
||||
* `naxsi_flag_libinjection_xss`
|
||||
|
||||
Since `0.56`, naxsi as well support JSON output from blocks/events with enable/disable flags at runtime
|
||||
* `naxsi_json_log`
|
||||
|
||||
### Gentle reminder
|
||||
It is important to know that naxsi operates at the REWRITE phase of nginx. Thus, setting those variables directly in the location where naxsi is present is ineffective (as naxsi will be called before variable set is effective).
|
||||
|
||||
This is correct:
|
||||
|
||||
```
|
||||
set $naxsi_flag_enable 0;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
But this is *wrong*:
|
||||
```
|
||||
location / {
|
||||
set $naxsi_flag_learning 1;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
With that said, you can use the power of nginx, lua, etc. to change naxsi's behavior. The presence of these variables will enable/disable learning mode, naxsi itself or force extensive logging.
|
||||
You can thus do things naxsi is usually not able to, like modifying its behavior according to (nginx) variables set at run-time :
|
||||
|
||||
```
|
||||
# Disable naxsi if client ip is 127.0.0.1
|
||||
if ($remote_addr = "127.0.0.1") {
|
||||
set $naxsi_flag_enable 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Those variables can as well be set from lua scripts (see nginx's mod_lua).
|
||||
|
||||
### naxsi_flag_learning
|
||||
|
||||
If `naxsi_flag_learning` variable is present, this value will override naxsi's current static configuration regarding learning mode.
|
||||
|
||||
```
|
||||
if ($remote_addr = "1.2.3.4") {
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
location / {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### naxsi_flag_post_action
|
||||
|
||||
[post_action](http://wiki.nginx.org/HttpCoreModule#post_action) can be used by naxsi to literally forward a request to the [DeniedUrl](directives.md#deniedurl) location. It is on by default until naxsi 0.50 (a souvenir from ̀nx_intercept`) and is off by default since 0.51, because of the switch to [nxtool](https://github.com/nbs-system/naxsi/tree/master/nxapi).
|
||||
Using this might lead to unpredictable behavior
|
||||
Can be set to 0 or 1
|
||||
|
||||
### naxsi_flag_enable
|
||||
|
||||
If `naxsi_flag_enable` variable is present and set to 0, naxsi will be disabled in this request. This allows you to partially disable naxsi in specific conditions.
|
||||
To completely disable naxsi for "trusted" users :
|
||||
|
||||
```
|
||||
set $naxsi_flag_enable 0;
|
||||
location / {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### naxsi_extensive_log
|
||||
|
||||
If present (and set to “1”), this variable will force naxsi to log the CONTENT of variable matching rules.
|
||||
Because of a potential impact on performance, use this with caution. Naxsi will log those to nginx’s `error_log`, ie:
|
||||
|
||||
```
|
||||
NAXSI_EXLOG: ip=%V&server=%V&uri=%V&id=%d&zone=%s&var_name=%V&content=%V
|
||||
```
|
||||
|
||||
See [naxsi logs](naxsilogs.md) for more details.
|
||||
|
||||
|
||||
### naxsi_flag_libinjection_sql
|
||||
|
||||
If set to "1", naxsi will pass every parsed content to [libinjection](libinjection-integration.md) and ask for SQL injection detection.
|
||||
If the libinjection matches, internal rule [libinjection_sql is fired ](internal-rules.md#libinjection_sql).
|
||||
|
||||
### naxsi_flag_libinjection_xss
|
||||
|
||||
If set to "1", naxsi will pass every parsed content to [libinjection](libinjection-integration.md) and ask for XSS detection.
|
||||
If the libinjection matches, internal rule [libinjection_xss is fired ](internal-rules.md#libinjection_xss).
|
||||
|
||||
|
||||
### naxsi_json_log
|
||||
|
||||
If present (and set to “1”), this variable will force naxsi to log in JSON format of events/blocks.
|
||||
JSON output will be limited to 1948, if it will be more to log, second entry (multiline log) will be empty JSON. Makes no sense to connect big JSONs on multiline.
|
||||
|
||||
So if a multiline event is made, I send an empty JSON. You cannot connect JSON across logs lines, this would be very difficult for JSON parsing.
|
||||
|
||||
I think this will help in most of cases.
|
||||
|
||||
Multiline log example:
|
||||
|
||||
```
|
||||
2020/07/22 09:24:57 [error] 14714#0: *1 NAXSI_FMT: ip=127.0.0.1&server=localhost&uri=/&vers=0.56&total_processed=1&total_blocked=1&config=learning&cscore0=$SQL&score0=630&zone0=ARGS&id0=1998&var_name0=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1&zone1=ARGS&id1=1998&var_name1=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2&zone2=ARGS&id2=1998&var_name2=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3&zone3=ARGS&id3=1998&var_name3=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4&zone4=ARGS&id4=1998&var_name4=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa5&zone5=ARGS&id5=1998&var_name5=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa6&zone6=ARGS&id6=1998&var_name6=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7&zone7=ARGS&id7=1998&var_name7=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa8&zone8=ARGS&id8=1998&var_name8=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9&zone9=ARGS&id9=1998&var_name9=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10&zone10=ARGS&id10=1998&var_name10=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa11&zone11=ARGS&id11=1998&var_name11=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa12&zone12=ARGS&id12=1998&var_name12=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa13&seed_start=185, client: 127.0.0.1, server: localhost, request: "GET /?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
2020/07/22 09:24:57 [error] 14714#0: *1 NAXSI_FMT: seed_end=185&zone13=ARGS&id13=1998&var_name13=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa14&zone14=ARGS&id14=1998&var_name14=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa15, client: 127.0.0.1, server: localhost, request: "GET /?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA10=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA12=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA13=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA14=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA15=1998 HTTP/1.1", host: "localhost"
|
||||
As you can see they're connected by "seed_start" and "seed_end".
|
||||
```
|
||||
|
||||
Naxsi will just return empty JSON:
|
||||
|
||||
```
|
||||
2020/07/22 09:24:57 [error] 14714#0: *1 { }, client: 127.0.0.1, server: localhost, request: "GET /?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA10=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA12=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA13=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA14=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA15=1998 HTTP/1.1", host: "localhost"
|
||||
2020/07/22 09:24:57 [error] 14714#0: *1 { }, client: 127.0.0.1, server: localhost, request: "GET /?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA10=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA12=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA13=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA14=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA15=1998 HTTP/1.1", host: "localhost"
|
||||
```
|
||||
|
||||
My use case is DataDog (which parser cannot connect multiple JSON with "seed_start" and "seed_end" values, this would be also very complex to connect and parse in DataDog)
|
||||
|
||||
The typical case should look like this (no Multiline log)
|
||||
|
||||
```
|
||||
2020/07/22 09:25:01 [error] 14714#0: *2 { "ip":"127.0.0.1","server":"localhost","uri":"/","vers":"0.56","total_processed":"2","total_blocked":"2","config":"learning","cscore0":"$XSS","score0":"16","zone0":"ARGS","id0":"1302","var_name0":"","zone1":"ARGS","id1":"1303","var_name1":"" }, client: 127.0.0.1, server: localhost, request: "GET /?<script> HTTP/1.1", host: "localhost"
|
||||
```
|
||||
@@ -0,0 +1,22 @@
|
||||
FIXMEFIXME
|
||||
|
||||
|
||||
# Examples
|
||||
|
||||
This will look for the string `Submit=Run` on the url `/script`, with the *POST* variable `Submit` present:
|
||||
```
|
||||
MainRule "msg:detection Submit=Run in POST" "str:Submit=Run" "mz:$URL:/script|$BODY_VAR:Submit" "s:$ATTACK" id: 1230001;
|
||||
```
|
||||
|
||||
This will look for accesses on the `/hidden.html` url:
|
||||
```
|
||||
MainRule "msg:detection URL-Access" "str:/hidden.html" "mz:URL" "s:$ATTACK" id:1230002;
|
||||
```
|
||||
|
||||
This will detect the string `jjoplmh` in the `cms` *GET* variable:
|
||||
```
|
||||
MainRule "str:jjoplmh" "msg:Possible Wordpress-Plugin-Backdoor detected" "mz:$ARGS_VAR:cms" "s:$UWA:8" id:42000347;
|
||||
```
|
||||
|
||||
naxsi_core.rules contains examples of rules. See as well mex's [[Doxi rules | https://bitbucket.org/lazy_dogtown/doxi-rules]] for more rules examples (doxi is third party rules that will focus on emerging threats).
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Whitelists
|
||||
|
||||
Whitelists are meant to instruct naxsi to ignore specific patterns in specific context(s) to avoid false positives.
|
||||
ie. Allow the ' character in the field named `term` at url /search :
|
||||
`BasicRule wl:1013 "mz:$ARGS_VAR:term|$URL:/search";`
|
||||
|
||||
Whitelists can be present at `location` level (BasicRule) or at `http` level (MainRule).
|
||||
Whitelists have the following syntax :
|
||||
|
||||

|
||||
|
||||
Everything must be quoted with double quotes, except the wl part.
|
||||
|
||||
### Whitelisted ID (wl:...)
|
||||
|
||||
Which rule ID(s) are whitelisted. Possible syntax are:
|
||||
|
||||

|
||||
|
||||
* `wl:0` : Whitelist all rules
|
||||
* `wl:42` : Whitelist rule `#42`
|
||||
* `wl:42,41,43` : Whitelist rules `42`, `41` and `43`
|
||||
* `wl:-42` : Whitelist all user rules (`>= 1000`), excepting rule `42`
|
||||
|
||||
_note: you can't mix negative and positive ID(s) in whitelists_
|
||||
|
||||
### MatchZone (mz:...)
|
||||
|
||||
Please refer to [Match Zones](matchzones-bnf.md) for details.
|
||||
|
||||
*mz* is the match-zone, specifying in which part(s) of the request the specified ID(s) must be ignored.
|
||||
|
||||
In whitelist context, all conditions specificied in the *mz* must be satisfied :
|
||||
|
||||
```
|
||||
BasicRule wl:4242 "mz:$ARGS_VAR:foo|$URL:/x";
|
||||
```
|
||||
_ignore id 4242 in GET var named `foo` only on URL `/x`_
|
||||
|
||||
As for rules, `$URL*` in match-zone is not enough to specify the target zone.
|
||||
|
||||
|
||||
### Notes
|
||||
|
||||
- A zone (ARGS,BODY,HEADERS) can be suffixed with `|NAME`, meaning the rule matched in the name of the variable, but not its content.
|
||||
- `RAW_BODY` whitelists are written just as any `BODY` whitelist, see [Whitelists Examples](whitelists-examples.md)
|
||||
- A whitelist can't mix `_X` elements with `_VAR` or `$URL` items. ie:
|
||||
|
||||
```
|
||||
$URL_X:/foo|$ARGS_VAR:bar : WRONG
|
||||
$URL_X:^/foo$|$ARGS_VAR_X:^bar$ : GOOD
|
||||
```
|
||||
|
||||
You can also whitelist by IP/CIDR and all the rules will not be blocked for these ips but logs will be generated.
|
||||
For more details look here: [IgnoreIP and IgnoreCIDR](IgnoreIP-and-IgnoreCIDR.md)
|
||||
@@ -0,0 +1,119 @@
|
||||
Go to [Whitelists Explanation](whitelists-bnf.md)
|
||||
|
||||
### Static Whitelist Examples
|
||||
|
||||
Totally disable rule #1000 for this location, matchzone is empty, so the whitelist always matches.
|
||||
|
||||
```
|
||||
BasicRule wl:1000;
|
||||
```
|
||||
|
||||
Disable rule #1000 on all url in GET argument named `foo`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR:foo";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in GET argument named `foo` for url `/bar`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR:foo|$URL:/bar";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments for url `/bar`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$URL:/bar|ARGS";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET argument NAMES (only name, not content):
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:ARGS|NAME";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET argument NAMES (only name, not content) for url `/bar`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$URL:/bar|ARGS|NAME";
|
||||
```
|
||||
|
||||
|
||||
### Regex Whitelist Examples (>= 0.52)
|
||||
|
||||
Disable rule `#1000` in all GET arguments containing `meh`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:meh";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in GET argument starting with `meh`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:^meh";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments matching `meh_<number>`:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$ARGS_VAR_X:^meh_[0-9]+$"
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments for URL starting with /foo:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$URL_X:^/foo|ARGS";
|
||||
```
|
||||
|
||||
Disable rule `#1000` in all GET arguments starting with a number for URL starting with /foo:
|
||||
|
||||
```
|
||||
BasicRule wl:1000 "mz:$URL_X:^/foo|$ARGS_VAR_X:^[0-9]";
|
||||
```
|
||||
|
||||
|
||||
### RAW_BODY whitelists
|
||||
|
||||
See [RAW_BODY](rawbody.md) specificites first.
|
||||
|
||||
Whitelists targeting RAW_BODY are written in the same way as any other BODY rule.
|
||||
|
||||
With the following rule targeting RAW_BODY :
|
||||
```
|
||||
MainRule id:4241 s:DROP str:RANDOMTHINGS mz:RAW_BODY;
|
||||
```
|
||||
|
||||
Whitelisting id:4241 would be :
|
||||
```
|
||||
BasicRule wl:4241 "mz:$URL:/|BODY";
|
||||
```
|
||||
|
||||
### FILE_EXT whitelists
|
||||
|
||||
See [FILE_EXT](zoom-fileext.md) specifities first.
|
||||
|
||||
Whitelisting rule 1337 on URL /index.html for file name will be written :
|
||||
|
||||
```
|
||||
BasicRule wl:1337 "mz:$URL:/index.html|FILE_EXT";
|
||||
```
|
||||
|
||||
### JSON whitelists
|
||||
|
||||
See [JSON](json.md) specifities first.
|
||||
|
||||
JSON is handled as normal BODY, and parsed into variable when possible :
|
||||
|
||||
```
|
||||
BasicRule wl:1302 "mz:$BODY_VAR:lol";
|
||||
```
|
||||
|
||||
Would whitelist for the following json body :
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"lol" : "foo<bar"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# **Packaging Naxsi**
|
||||
|
||||
This section describes how to build naxsi from source and package it for various distros.
|
||||
|
||||
## Packaging for Ubuntu and Debian Linux.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> Some Ubuntu & Debian releases, like *Mantic* and *Bookworm*, uses **`libpcre2-dev`** instead of `libpcre3-dev`.
|
||||
|
||||
```bash
|
||||
# first fetch all required dependencies
|
||||
sudo apt-get -qqy --no-install-recommends install \
|
||||
build-essential ca-certificates dpkg-dev \
|
||||
gzip git libgd-dev libgeoip-dev libpcre3-dev \
|
||||
libssl-dev libxslt1-dev nginx tar wget zlib1g-dev
|
||||
|
||||
# second fetch the nginx sources via apt
|
||||
apt-get source nginx
|
||||
|
||||
# clone naxsi repo with all submodules
|
||||
git clone --recurse-submodules https://github.com/wargio/naxsi.git
|
||||
|
||||
# fetch some missing tools; ps: if you want to help to have a proper deb build, feel free to submit a PR.
|
||||
git clone --depth=1 https://github.com/wargio/deb-creator.git
|
||||
|
||||
# setup nginx build options and deb values.
|
||||
mkdir -p deb_pkg/
|
||||
DEB_PKG=$(realpath deb_pkg)
|
||||
NAXSI_VERSION=$(grep "NAXSI_VERSION" naxsi/naxsi_src/naxsi_const.h | cut -d ' ' -f3 | sed 's/"//g')
|
||||
LIBPCRE_PACKAGE="libpcre3"
|
||||
LIBPCRE_VERSION=$(dpkg -s $LIBPCRE_PACKAGE | grep '^Version:' | cut -d ' ' -f2 | cut -d '-' -f1)
|
||||
NGINX_VERSION=$(dpkg -s nginx | grep '^Version:' | cut -d ' ' -f2 | cut -d '-' -f1)
|
||||
NGINX_BUILD_OPTS=$(nginx -V 2>&1 | grep "configure arguments:" | cut -d ":" -f2- | sed -e "s#/build/nginx-[A-Za-z0-9]*/#./#g" | sed 's/--add-dynamic-module=[A-Za-z0-9\/\._-]*//g')
|
||||
echo "LIBPCRE_PACKAGE: $LIBPCRE_PACKAGE"
|
||||
echo "LIBPCRE_VERSION: $LIBPCRE_VERSION"
|
||||
echo "NGINX_VERSION: $NGINX_VERSION"
|
||||
echo "NGINX_BUILD_OPTS: $NGINX_BUILD_OPTS"
|
||||
echo "Press CTRL+C if you see something wrong with the version or build options"
|
||||
sleep 5
|
||||
|
||||
# build module
|
||||
cd nginx-$NGINX_VERSION
|
||||
CMDLINE=$(echo ./configure $NGINX_BUILD_OPTS --add-dynamic-module=../naxsi/naxsi_src/)
|
||||
eval $CMDLINE
|
||||
make modules
|
||||
cd ..
|
||||
|
||||
# install files in the temporary folder
|
||||
mkdir -p "$DEB_PKG/data/usr/lib/nginx/modules/"
|
||||
mkdir -p "$DEB_PKG/data/usr/share/nginx/modules-available/"
|
||||
mkdir -p "$DEB_PKG/data/usr/share/naxsi/whitelists"
|
||||
mkdir -p "$DEB_PKG/data/usr/share/naxsi/blocking"
|
||||
install -Dm755 naxsi/distros/deb/postinstall.script "$DEB_PKG/postinstall.script"
|
||||
install -Dm755 naxsi/distros/deb/postremove.script "$DEB_PKG/postremove.script"
|
||||
install -Dm755 naxsi/distros/deb/preremove.script "$DEB_PKG/preremove.script"
|
||||
install -Dm644 naxsi/distros/deb/control.install "$DEB_PKG/control.install"
|
||||
install -Dm755 "nginx-$NGINX_VERSION/objs/ngx_http_naxsi_module.so" "$DEB_PKG/data/usr/lib/nginx/modules/ngx_http_naxsi_module.so"
|
||||
install -Dm644 naxsi/distros/deb/mod-http-naxsi.conf "$DEB_PKG/data/usr/share/nginx/modules-available/mod-http-naxsi.conf"
|
||||
install -Dm644 naxsi/distros/nginx/naxsi_block_mode.conf "$DEB_PKG/data/usr/share/naxsi/naxsi_block_mode.conf"
|
||||
install -Dm644 naxsi/distros/nginx/naxsi_denied_url.conf "$DEB_PKG/data/usr/share/naxsi/naxsi_denied_url.conf"
|
||||
install -Dm644 naxsi/distros/nginx/naxsi_learning_mode.conf "$DEB_PKG/data/usr/share/naxsi/naxsi_learning_mode.conf"
|
||||
install -Dm644 naxsi/naxsi_rules/naxsi_core.rules "$DEB_PKG/data/usr/share/naxsi/naxsi_core.rules"
|
||||
install -Dm644 naxsi/naxsi_rules/whitelists/*.rules "$DEB_PKG/data/usr/share/naxsi/whitelists"
|
||||
install -Dm644 naxsi/naxsi_rules/blocking/*.rules "$DEB_PKG/data/usr/share/naxsi/blocking"
|
||||
|
||||
# add deb details and info.
|
||||
sed -i "s/@NGINX_VERSION@/$NGINX_VERSION/" "$DEB_PKG/control.install"
|
||||
sed -i "s/@LIBPCRE_PACKAGE@/$LIBPCRE_PACKAGE/" "$DEB_PKG/control.install"
|
||||
sed -i "s/@LIBPCRE_VERSION@/$LIBPCRE_VERSION/" "$DEB_PKG/control.install"
|
||||
sed -i "s/@NAXSI_VERSION@/$NAXSI_VERSION/" "$DEB_PKG/control.install"
|
||||
|
||||
# build deb file
|
||||
./deb-creator/deb-creator "$DEB_PKG"
|
||||
```
|
||||
|
||||
## Packaging for Arch Linux.
|
||||
|
||||
```bash
|
||||
# fetch the needed dependencies
|
||||
pacman -Syy --needed --noconfirm sudo wget base-devel git
|
||||
|
||||
# fetch PKGBUILD (you can also use tags) instead of the main branch
|
||||
wget https://raw.githubusercontent.com/wargio/naxsi/refs/heads/main/distros/arch/PKGBUILD
|
||||
|
||||
# build the package
|
||||
makepkg -s
|
||||
```
|
||||
|
||||
# Go Back
|
||||
|
||||
[Table of Contents](index.md).
|
||||
@@ -0,0 +1,81 @@
|
||||
# **Naxsi Rules**
|
||||
|
||||
A Naxsi rule is a search pattern which is applied to a request to detect malicious behaviour.
|
||||
|
||||
A rule is defined by `MainRule` or `BasicRule` directive, an **id**, a **score**, a **search parameter** (i.e. case-insensitive string or regex), a **matchzone** and an **optional description**.
|
||||
|
||||
Example of rule:
|
||||
|
||||
```bash
|
||||
MainRule id:12345 "s:$FOO:8,$BAR:4" "str:malicious" "mz:URL" "msg:string rule description";
|
||||
BasicRule id:67890 "s:$TOO:4" "rx:[a-z]{5}" "mz:ARGS" "msg:regex rule description";
|
||||
```
|
||||
|
||||
## **`MainRule` and `BasicRule` directives**
|
||||
|
||||
As explained in the directives chapter we can have 2 kinds of rules:
|
||||
|
||||
- [A **global** rule defined by the `MainRule` directive](directives.md#mainrule)
|
||||
- [A **location-specific** rule defined by the `BasicRule` directive](directives.md#basicrule)
|
||||
|
||||
These two directives are mandatory to define rule.
|
||||
|
||||
## **Rule Id**
|
||||
|
||||
The rule identifier, which identifies triggered rules for blocked requests, must always be a positive integer greater than 999. These identifiers follows the format `id:<number>`, for example `id:12345`.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> It is possible to use the same rule id multiple times, but **it is NOT suggested**, because these **ids** are used for logging and whitelisting.
|
||||
|
||||
Naxsi has some internal rules that are hardcoded within the WAF; these rules are defined by the reserved **ids** from **1 to 1000**.
|
||||
|
||||
You can read more about them in the [Internal Rules](internal_rules.md) chapter.
|
||||
|
||||
## **Rule Score**
|
||||
|
||||
Each rule must define a score which is used to increase the [`CheckRule` counter](directives.md#checkrule) for each request.
|
||||
|
||||
> 💡 Tip
|
||||
>
|
||||
> It is possible to assign multiple scores for each rule; just add a comma between score values.
|
||||
|
||||
Scores follows the format `"s:$<name>:<value>[,$<name>:<value>,...]"`, for example `"s:$FOO:8,$BAR:4,$BAZ:4"`.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> If there are no `CheckRule` defined for a given **variable** the rule will never trigger an action (this applies only to user-defined rules).
|
||||
|
||||
## **Search Parameter**
|
||||
|
||||
Each rule must define a search parameter which can be expressed as a string or regular expression (regex); These search parameters are expressed as `"str:<string>"` for a string or `"rx:<regex>"` for a regular expression.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> **Strings are always case insensitive**, so `"str:foo"` will match `foo` but also `FoO` and `FOO`.
|
||||
>
|
||||
> Regular expressions (regexes) follows the [PCRE format](https://www.pcre.org/) and may require escaping due how nginx parses the config files. Also **regexes are always case insensitive and multiline**, so `"rx:[a-z]+"` will match `foo` but also `FoO` and `FOO`.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> Naxsi decodes any `url-encoded` or `hexadecimal` sequence, this means the string or regex to search for must be of the decoded content (**this applies also to URLs**).
|
||||
>
|
||||
> Example: Let's take the content `1%20UnioN%20SeLEct%201`, this will be decoded to `1 UnioN SeLEct 1` and it will match `"str:union select"` and `"rx:union|select"`.
|
||||
|
||||
## **Matchzone**
|
||||
|
||||
Matchzones defines where a search parameter shall be used for each rule.
|
||||
|
||||
For more information and review its format, refer to the [Matchzones chapter](matchzones.md).
|
||||
|
||||
## **Optional description**
|
||||
|
||||
Each rule can also have a description which is expressed as `"msg:<description>"`, for example `"msg:Example of description"`.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> **Descriptions are not used by Naxsi**, but can be used to explain what the rule does.
|
||||
|
||||
# Go Back
|
||||
|
||||
[Table of Contents](index.md).
|
||||
@@ -0,0 +1,57 @@
|
||||
# **Naxsi Whitelist**
|
||||
|
||||
A Naxsi whitelist is a matchzone which negates one or multiple rules via their ids.
|
||||
|
||||
A whitelist is defined by `MainRule` or `BasicRule` directive like for rules, one or multiple **ids** (comma separated) and _optionally_ a **matchzone**.
|
||||
|
||||
Example of rule:
|
||||
|
||||
```bash
|
||||
MainRule wl:12345,3333 "mz:URL";
|
||||
BasicRule wl:67890 "mz:ARGS|BODY";
|
||||
```
|
||||
|
||||
You can also whitelist by **IP/CIDR** and all the rules will not be blocked for these IPs but logs will be generated.
|
||||
For more details look at [`IgnoreIP`](directives.md#ignoreip) and [`IgnoreCIDR`](directives.md#ignorecidr) directives.
|
||||
|
||||
## **`MainRule` and `BasicRule` directives**
|
||||
|
||||
As explained in the directives chapter we can have 2 kinds of whitelists:
|
||||
|
||||
- [A **global** whitelist defined by the `MainRule` directive](directives.md#mainrule)
|
||||
- [A **location-specific** whitelist defined by the `BasicRule` directive](directives.md#basicrule)
|
||||
|
||||
These two directives are mandatory to define whitelist.
|
||||
|
||||
## **Whitelist Ids**
|
||||
|
||||
The whitelist identifiers are used to define which rules to whitelist; the **ids** are **comma separated** and identifies follows the format `wl:<number>`, for example `wl:12345,78894`.
|
||||
|
||||
> ℹ️ Info
|
||||
>
|
||||
> It is possible to use define a whitelist with a negative **id**; when defined the whitelist will match all the rules (`> 999`), excepting the rule whitelisted.
|
||||
|
||||
Examples:
|
||||
|
||||
* `wl:0`: Whitelist all rules.
|
||||
* `wl:1234`: Whitelist rule `1234`.
|
||||
* `wl:1234,4567,7890`: Whitelist rules `1234`, `4567` and `7890`.
|
||||
* `wl:-8888`: Whitelist all user rules (`> 999`), but rule `8888`.
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> It is not possible to mix negative and positive **ids** in one whitelist.
|
||||
|
||||
## **Matchzone**
|
||||
|
||||
Matchzones defines where a whitelist should apply for each given **id**; these operate under an **AND** logic (like `url` must be `/foo` AND must occur in `ARGS`)
|
||||
|
||||
> 📣 Important
|
||||
>
|
||||
> This parameter is **optional**; when not defined the rule is never applied.
|
||||
|
||||
For more information and review its format, refer to the [Matchzones chapter](matchzones.md).
|
||||
|
||||
# Go Back
|
||||
|
||||
[Table of Contents](index.md).
|
||||