Gixy是一个分析Nginx配置的工具。Gixy的主要目标是防止安全性错误配置并自动进行缺陷检测。
目前支持的Python版本为2.7和3.5+。
免责声明:Gixy仅在GNU/Linux上经过良好测试,其他操作系统可能存在一些问题。
目前在Gixy可以找到以下问题:
您应该避免使用多行响应头,原因如下:
配置错误的示例:
# http://nginx.org/en/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';
唯一的解决方案是永远不要使用多行响应头。
不幸的是,许多人不知道指令的继承是如何工作的。大多数情况下,这会导致add_header
在尝试在嵌套级别添加新响应头时滥用指令。Nginx 文档中提到了此功能:
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.
逻辑非常简单:如果您在一个级别(例如,在server
部分)设置了响应头,然后在较低的级别location
(比方说)中设置了其他的响应头,那么第一个响应头将被丢弃。
这很容易检查:
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 /
(X-Frame-Options
消息头在服务器响应中):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 /new-headers
(请求头中有Cache-Control
和Pragma
,但没有X-Frame-Options
)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
有几种方法可以解决这个问题:
server
部分是一个不错的选择)alias指令用于替换指定位置的路径。例如,配置如下:
location /i/ {
alias /data/w3/images/;
}
在/i/top.gif
的请求下,会发送/data/w3/images/top.gif
文件。
但是,如果location不以目录分隔符(即/)结尾,则:
location /i {
alias /data/w3/images/;
}
在/ i .. / app/config.py的请求下,会发送/ data/w3/app/config.py文件。
换句话说,错误的alias
配置可以允许攻击者读取存储在目标文件夹之外的文件。
这很简单:
alias
指令;通常,位于nginx后面的应用程序需要正确的Host
头来生成URL(重定向、资源、邮件中的链接等)。欺骗此协议头,可能导致从网络钓鱼到SSRF等各种问题。
注意:您的应用程序也可以使用
X-Forwarded-Host
协议头来实现此功能。在这种情况下,您必须确保协议头设置正确;
大多数情况下它使用$http_host
变量代替$host
而且它们是完全不同的:
$host
- HOST按此优先顺序排列:来自请求行的host名,或来自“Host”请求标头字段的host名,或与请求匹配的服务器名称;$http_host
- "Host"请求头location @app {
proxy_set_header Host $http_host;
# Other proxy params
proxy_pass http://backend;
}
### 如何解决server name
指令列出正确的服务器名$host
而不是$http_host
HTTP Splitting - 使用不正确验证的输入进行攻击。它通常针对位于Nginx(HTTP请求拆分)或其用户(HTTP响应拆分)的Web应用程序。
当攻击者将新行字符\n或\r\n插入到Nginx创建的请求或响应中时,就会创建该漏洞。
你应该时刻注意:
rewrite
, return
, add_header
, proxy_set_header
或 proxy_pass
;$uri
和 $document_uri
变量,以及使用它们的指令,因为这些变量会包含URL-encoded解码后的值;server {
listen 80 default;
location ~ /v1/((?<action>[^.]*)\.json)?$ {
add_header X-Action $action;
return 200 "OK";
}
}
利用点:
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
正如您所看到的,攻击者可以添加响应头x-crlf-header: injected
,可能的原因是:
add_header
不编码或验证输入值$action
值由特定范围内的正则表达式给定: [^.]*;$action
的值等于see below下面的x-crlf-header: injected
,并且在其使用时添加了响应头。$request_uri
代替$uri
/some/(?<action>[^/\s]+)
代替/some/(?<action>[^/]+
$uri
可能是一个不错的办法(除非你确信你知道你在做什么)。使用正则表达式验证Referer
或Origin
协议头并不罕见,通常需要设置X-Frame-Options
协议头(ClickJacking保护)或跨源资源共享。
这种配置中最常见的错误是:
注意:默认情况下,Gixy不会检查第三方来源匹配的正则表达式。您可以使用
--original-domain example.com,for.bar
选项传递受信任的域列表
$http_origin
或 $http_referer
的所有if
指令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';
}
Referer
请求头使用正则表达式验证,可以使用ngx_http_referer_module
模块map
指令服务器端请求伪造攻击,迫使服务器执行任意请求(在我们的例子中来自Nginx)。当攻击者控制代理服务器的地址(proxy_pass
指令的第二个参数),这是可能发生的。
有两种类型的错误会使服务器容易受到攻击:
经典的错误配置是缺乏internal
指令,这使SSRF成为可能:
location ~ /proxy/(.*)/(.*)/(.*)$ {
proxy_pass $1://$2/$3;
}
攻击者可以完全控制代理地址,这样就可以代表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;
}
根据Nginx文档,内部请求如下:
- 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
因此,任何不安全重写会导致攻击者发起内部请求并控制被代理服务器的地址。
错误的配置如下:
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;
}
在编写此类配置时,您应该遵循以下几个规则:
map
选择或以其他方式进行;模块ngx_http_referer_module允许阻止对具有错误Referer
值的请求的服务访问。它通常用于设置X-Frame-Options header
(点击劫持保护),但可能还有其他情况。
配置该模块的典型问题:
server_names
错误的服务器名称(server_name指令);注意:目前,Gixy只能检测出none作为有效referer的用法
根据文档:
none-请求头中缺少“Referer”字段;
不过,重要的是要记住,任何资源都可以让用户的浏览器在没有Referer请求头的情况下发起请求。例如: