Added eng documentation.

pull/23/head
Yaroslav Buchnev 2017-05-10 18:44:01 +03:00
parent 0ba9b217d8
commit c5094571f8
10 changed files with 396 additions and 3 deletions

109
docs/en/README.md Normal file
View File

@ -0,0 +1,109 @@
Gixy is a static analysis tool for Ngix config files. It is best at finding security misconfigurations, but can be also used to detect general errors.
## What it can do
Right now Gixy can find:
* [[ssrf] Server Side Request Forgery](https://github.com/yandex/gixy/blob/master/docs/en/plugins/ssrf.md)
* [[http_splitting] HTTP Splitting](https://github.com/yandex/gixy/blob/master/docs/en/plugins/httpsplitting.md)
* [[origins] Problems with referrer/origin validation](https://github.com/yandex/gixy/blob/master/docs/en/plugins/origins.md)
* [[add_header_redefinition] Redefining of upstream response headers with directive "add_header"](https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheaderredefinition.md)
* [[host_spoofing] Request's Host header forgery](https://github.com/yandex/gixy/blob/master/docs/en/plugins/hostspoofing.md)
* [[valid_referers] none in valid_referers](https://github.com/yandex/gixy/blob/master/docs/en/plugins/validreferers.md)
* [[add_header_multiline] Multiline response headers](https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheadermultiline.md)
You can find things that Gixy is learning to detect at [Issues labeled with "new plugin"](https://github.com/yandex/gixy/issues?q=is%3Aissue+is%3Aopen+label%3A%22new+plugin%22)
##Installation
The easiest way to install Gixy is to use pip:
```bash
pip install gixy
```
The tool is compatible with Python 2.7/3.5/3.6
## Using Gixy
After installation you can find `gixy` in your command line. Buy default Gixy is working with `/etc/nginx/nginx.conf` folder, which is standart for Nginx installations, but you can provide a custom path as an argument:
```
$ gixy /www/configs/nginx/nginx.conf
```
==================== Results ===================
Problem: [http_splitting] Possible HTTP-Splitting vulnerability.
Description: Using variables that can contain "\n" may lead to http injection.
Additional info: https://github.com/yandex/gixy/wiki/ru/httpsplitting
Reason: At least variable "$action" can contain "\n"
Pseudo config:
include /etc/nginx/sites/default.conf;
server {
location ~ /v1/((?<action>[^.]*)\.json)?$ {
add_header X-Action $action;
}
}
==================== Summary ===================
Total issues:
Unspecified: 0
Low: 0
Medium: 0
High: 1
```
Gixy can process `include` directive and tries to handle all the dependencies. If something went wrong, you can launch Gixy with the `d` flag, which enables debug mode for extra information.
To view all options:
```
$ gixy -h
usage: gixy [-h] [-c CONFIG_FILE] [--write-config CONFIG_OUTPUT_PATH]
[-v] [-l] [-f {console,text,json}] [-o OUTPUT_FILE] [-d]
[--tests TESTS] [--skips SKIPS] [--disable-includes]
[--origins-domains domains]
[--origins-https-only https_only]
[--add-header-redefinition-headers headers]
[nginx.conf]
Gixy - a Nginx configuration [sec]analyzer
positional arguments:
nginx.conf Path to nginx.conf, e.g. /etc/nginx/nginx.conf
optional arguments:
-h, --help show this help message and exit
-c CONFIG_FILE, --config CONFIG_FILE
config file path
--write-config CONFIG_OUTPUT_PATH
takes the current command line args and writes them
out to a config file at the given path, then exits
-v, --version show program's version number and exit
-l, --level Report issues of a given severity level or higher (-l
for LOW, -ll for MEDIUM, -lll for HIGH)
-f {console,text,json}, --format {console,text,json}
Specify output format
-o OUTPUT_FILE, --output OUTPUT_FILE
Write report to file
-d, --debug Turn on debug mode
--tests TESTS Comma-separated list of tests to run
--skips SKIPS Comma-separated list of tests to skip
--disable-includes Disable "include" directive processing
plugins options:
--origins-domains domains
Default: *
--origins-https-only https_only
Default: False
--add-header-redefinition-headers headers
Default: content-security-policy,x-xss-
protection,x-frame-options,x-content-type-
options,strict-transport-security,cache-control
available plugins:
host_spoofing
add_header_multiline
http_splitting
valid_referers
origins
add_header_redefinition
ssrf
```

View File

@ -0,0 +1,25 @@
# [add_header_multiline] Multiline response headers
You should avoid using multiline response headers, because:
* they sre considered depricated (см. [RFC 7230](https://tools.ietf.org/html/rfc7230#section-3.2.4));
* Some HTTP-clients and web browser never supported them (e.g. IE/Edge/Nginx).
## How can I find it?
Misconfiguration example:
```nginx
# http://nginx.org/ru/docs/http/ngx_http_headers_module.html#add_header
add_header Content-Security-Policy "
default-src: 'none';
script-src data: https://yastatic.net;
style-src data: https://yastatic.net;
img-src data: https://yastatic.net;
font-src data: https://yastatic.net;";
# https://www.nginx.com/resources/wiki/modules/headers_more/
more_set_headers -t 'text/html text/plain'
'X-Foo: Bar
multiline';
```
## What can I do?
The only solution is to never use multiline response headers.

View File

@ -0,0 +1,65 @@
# [add_header_redefinition] Redefining of upstream response headers with directive "add_header"
Unfortunately, many people consider the use of `add_header` directive for headers redefining a good practice.
This approach is flawed, which is discussed in Nginx [docs](http://nginx.org/ru/docs/http/ngx_http_headers_module.html#add_header):
> There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.
The logic is quite simple: if you set headers at one level (for example, in `server` section) and then at a lower level (let's say `location`) you set some other headers, then the first group won't apply.
It's easy to observe:
- Configuration:
```nginx
server {
listen 80;
add_header X-Frame-Options "DENY" always;
location / {
return 200 "index";
}
location /new-headers {
# Add special cache control
add_header Cache-Control "no-cache, no-store, max-age=0, must-revalidate" always;
add_header Pragma "no-cache" always;
return 200 "new-headers";
}
}
```
- Location request `/` (`X-Frame-Options` header is in server response):
```http
GET / HTTP/1.0
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 09 Jan 2017 19:28:33 GMT
Content-Type: application/octet-stream
Content-Length: 5
Connection: close
X-Frame-Options: DENY
index
```
- Location request `/new-headers` (headers `Cache-Control` and `Pragma` are present, but there's no `X-Frame-Options`):
```http
GET /new-headers HTTP/1.0
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 09 Jan 2017 19:29:46 GMT
Content-Type: application/octet-stream
Content-Length: 11
Connection: close
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
new-headers
```
## What can I do?
There are several ways to solve this problem:
- dublicate important headers;
- set all headers at one level (`server` section is a good choice)
- use [ngx_headers_more](https://www.nginx.com/resources/wiki/modules/headers_more/) module.
No solution is perfect, so choose one based on your needs.

View File

@ -0,0 +1,31 @@
# [host_spoofing] Request's Host header forgery
Often, an application located behind Nginx needs a correct `Host` header for URL generation (redirects, resources, links in emails etc.).
An attacker can spoof this header, which leads to a variety of problems, from phishing to SSRF. To prevent this, avoid:
> Relience on `X-Forwarded-Host` request header;
> In this case you have to ensure the header is set correctly at proxies;
## How can I find it?
Most of the time it's a result of using `$http_host` variable instead of `$host`.
And they are quite different:
* `$http` - host in order of priority: host name from request string, host name form `Host` request header, or a server name, compliant to the request;
* `$http_host` - "Host" request header.
Config sample:
```nginx
location @app {
proxy_set_header Host $http_host;
# Other proxy params
proxy_pass http://backend;
}
```
## What can I do?
Luckly, all is quite obvious:
* list all the correct server names in `server name` directive;
* always use `$host` instead of `$http_host`.
## Additional info
* [Host of Troubles Vulnerabilities](https://hostoftroubles.com/)
* [Practical HTTP Host header attacks](http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html)

View File

@ -0,0 +1,51 @@
# [http_splitting] HTTP Splitting
HTTP Splitting - attack that use improper input validation. It usually targets web application located behind Nginx (HTTP Request Splitting) or its users (HTTP Response Splitting).
Vulnerability is created when an attacker can insert new line symbol "\n" into request or into response, created by Nginx server.
## How can I find it?
You should always pay attention to:
- variables that are used in directives, responsible for the request creation (for they may contain CRLF), e.g. `rewrite`, `return`, `add_header`, `proxy_set_header` or `proxy_pass`;
- `$uri` and `$document_uri` variables, and the directive that process them, because these variables contain url-encoded value;
- variables, that are selected from an exclusive range, e.g. `(?P<myvar>[^.]+)`.
An example of configuration that contains vriable, selected from an exclusive range:
```nginx
server {
listen 80 default;
location ~ /v1/((?<action>[^.]*)\.json)?$ {
add_header X-Action $action;
return 200 "OK";
}
}
```
Explotation:
```http
GET /v1/see%20below%0d%0ax-crlf-header:injected.json HTTP/1.0
Host: localhost
HTTP/1.1 200 OK
Server: nginx/1.11.10
Date: Mon, 13 Mar 2017 21:21:29 GMT
Content-Type: application/octet-stream
Content-Length: 2
Connection: close
X-Action: see below
x-crlf-header:injected
OK
```
As you can see, an attacker could add `x-crlf-header: injected` response header. This was possible because:
- `add_header` doesn't encode or validate input value on suggestion that author knows about the consiquences;
- the path value is normalized before processing of the location;
- `$action` value was given from a regexp with an exclusive range: `[^.]*`;
- as the result, `$action` value is equal to that of `see below\r\nx-crlf-header:injected` and on its use the header was added to the response.
## What can I do?
- try to use safe variables, e.g. `$request_uri` instead of `$uri`;
- forbid the use of the new line symbol in the exclusive range by using `/some/(?<action>[^/\s]+)` instead of `/some/(?<action>[^/]+`
- it could be a good idea to validate `$uri` (only if you're sure you know what are you getting into).

View File

@ -0,0 +1,30 @@
# [origins] Problems with referrer/origin validation
It's not unusual to use regexp for `Referer` or `Origin` headers validation.
Often it is needed for setting the `X-Frame-Options` header (ClickJacking protection) or Cross-Origin Resource Sharing.
The most common errors with this configuration are:
- regexp errors;
- 3rd-party domain permissions.
> By default Gixy doesn't check regexps for 3rd-party domains matching, cause it's unclear wether you can should them. You can pass a list of trusted domains by using the option `--origins-domains example.com,foo.bar`
## How can I find it?
"Eazy"-breezy:
- you have to find all the `if` directives that are in charge of `$http_origin` or `$http_referer` check;
- make sure your regexps are a-ok.
Misconfig example:
```nginx
if ($http_origin ~* ((^https://www\.yandex\.ru)|(^https://ya\.ru)/)) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
}
```
TODO(buglloc): cover typical regexp-writing problems
TODO(buglloc): Regex Ninja?
## What can I do?
Fix your regexp or toss it away.
If you use regexp validation for `Referer` request header, then, possibly (not 100%), you could use [ngx_http_referer_module](http://nginx.org/ru/docs/http/ngx_http_referer_module.html).

60
docs/en/plugins/ssrf.md Normal file
View File

@ -0,0 +1,60 @@
# [ssrf] Server Side Request Forgery
Server Side Request Forgery - attack that forces a server to perform requests on behalf of an attacker (Nginx in our case).
It's possible when an attacker controls the address of a proxied server (second argument of the `proxy_pass` directive).
## How can I find it?
There are two types of errors that make a server vulnerable:
- lack of the [internal](http://nginx.org/ru/docs/http/ngx_http_core_module.html#internal) directive. It is used to point out a location that can be used for internal requests only;
- unsafe internal redirection.
### Lack of the internal directive
Classical misconfig, based on lack of the internal directive, that makes SSRF possible:
```nginx
location ~ /proxy/(.*)/(.*)/(.*)$ {
proxy_pass $1://$2/$3;
}
```
An attacker has complete control over the proxied address, that makes sending requests on behalf of Nginx possible.
### Unsafe internal redirection
Let's say you have internal location in your config and that location uses some request data as proxied server's address.
E.g.:
```nginx
location ~* ^/internal-proxy/(?<proxy_proto>https?)/(?<proxy_host>.*?)/(?<proxy_path>.*)$ {
internal;
proxy_pass $proxy_proto://$proxy_host/$proxy_path ;
proxy_set_header Host $proxy_host;
}
```
According to Nginx docs, internal requests are:
> - requests redirected by the **error_page**, **index**, **random_index**, and **try_files** directives;
> - requests redirected by the “X-Accel-Redirect” response header field from an upstream server;
> - subrequests formed by the “include virtual” command of the ngx_http_ssi_module module and by the ngx_http_addition_module module directives;
> - requests changed by the **rewrite** directive.]>
Accordingly, any unsafe rewrite allows an attacker to make an internal request and control a proxied server's address.
Misconfig example:
```nginx
rewrite ^/(.*)/some$ /$1/ last;
location ~* ^/internal-proxy/(?<proxy_proto>https?)/(?<proxy_host>.*?)/(?<proxy_path>.*)$ {
internal;
proxy_pass $proxy_proto://$proxy_host/$proxy_path ;
proxy_set_header Host $proxy_host;
}
```
## What can I do?
There are everal rules you better follow when writing such configurations:
- use only `internal location` for proxying;
- if possible, forbid user data transmission;
- protect proxied server's address:
* if the quantity of proxied hosts is limited (when you have S3 or smth), you better hardcode them and choose them with `map` or do it some other way;
* if you can' list all possible hosts to proxy, you should sign the address.

View File

@ -0,0 +1,22 @@
# [valid_referers] none in valid_referers
Module [ngx_http_referer_module](http://nginx.org/ru/docs/http/ngx_http_referer_module.html) allows to block the access to service for requests with wrong `Referer~ value.
It's often used for setting `X-Frame-Options` header (ClickJacking protection), but there may be other cases.
Typical problems with this module's config:
* use of `server_names` with bad server name (`server_name` directive);
* too broad and/or bad regexps;
* use of `none`.
> At the moment, Gixy can only detect the use of `none` as a valid referer.
## Why none is bad?
According to [docs](http://nginx.org/ru/docs/http/ngx_http_referer_module.html#valid_referers):
> `none` - the “Referer” field is missing in the request header;
Still, it's important to remember that any resource can make user's browser to make a request without a `Referer` request header.
E.g.:
- in case of redirect from HTTP to HTTPS;
- by setting up a properc[Referrer Policy](https://www.w3.org/TR/referrer-policy/);
- a request with opaque origin, `data:` scheme, for example.
So, by using `none` as a valid referer, you nullify any attemps in refferer validation.

View File

@ -32,12 +32,12 @@ location ~* ^/internal-proxy/(?<proxy_proto>https?)/(?<proxy_host>.*?)/(?<proxy_
```
Согласно документации Nginx внутренними запросами являются:
> - запросы, перенаправленные директивами **error_page**, index, random_index и **try_files**;
> - запросы, перенаправленные директивами **error_page**, **index**, **random_index** и **try_files**;
> - запросы, перенаправленные с помощью поля “X-Accel-Redirect” заголовка ответа вышестоящего сервера;
> - подзапросы, формируемые командой “include virtual” модуля ngx_http_ssi_module и директивами модуля ngx_http_addition_module;
> - запросы, изменённые директивой **rewrite**.]>
Соответственно, любой "не осторожный" реврайт позволит злоумышленнику сделать внутренний запрос и контролировать адрес проксируемого сервера.
Соответственно, любой "неосторожный" реврайт позволит злоумышленнику сделать внутренний запрос и контролировать адрес проксируемого сервера.
Пример плохой конфигурации:
```nginx

View File

@ -19,4 +19,4 @@
- указав соответствующую [Referrer Policy](https://www.w3.org/TR/referrer-policy/);
- обращение с opaque origin, например, используя схему `data:`.
Таким образом, используя `none` в качестве валидного реферера вы сводиде на нет любые попытки валидации реферера.
Таким образом, используя `none` в качестве валидного реферера вы сводите на нет любые попытки валидации реферера.