如何让Nginx更安全?

文章目录

  • [一 使用最新版](#一 使用最新版)
  • [二 限制连接数量](#二 限制连接数量)
  • [三 限制请求频率](#三 限制请求频率)
  • [四 防止目录遍历](#四 防止目录遍历)
  • [五 隐藏 Nginx 版本号](#五 隐藏 Nginx 版本号)
  • [六 设置超时时间](#六 设置超时时间)
  • [七 仅允许域名访问](#七 仅允许域名访问)
      • [使用两个 `server` 块](#使用两个 server 块)
      • [使用 `if` 语句](#使用 if 语句)
      • [直接禁止 IP](#直接禁止 IP)
  • [八 限制 Nginx 请求方法](#八 限制 Nginx 请求方法)
      • [只允许 GET 和 POST](#只允许 GET 和 POST)
      • [使用 map 模块](#使用 map 模块)
  • [九 错误页面重定向](#九 错误页面重定向)
  • [十 日志保留半年](#十 日志保留半年)
  • [十一 设置缓冲区](#十一 设置缓冲区)
  • [十二 使用普通用户启动](#十二 使用普通用户启动)

网络安全的重要性不言而喻。

那么小伙伴们有没有在日常使用 Nginx 的时候,特意去关注下它的安全配置呢?

今天松哥和小伙伴们讨论一下如何安全的使用 Nginx,给大伙几个建议。

一 使用最新版

建议使用最新版的 Nginx,对于已经部署的 Nginx,要及时更新到最新版本,以确保所有已知的安全漏洞都已修补。

Nginx 下载地址:https://nginx.org/en/download.html

二 限制连接数量

Nginx 可以通过 limit_conn_zone 和 limit_conn 两个组件来对客户端访问目录和文件的访问频率和次数进行限制,两个模块都能够对客户端访问进行限制,具体如何使用要结合公司业务环境进行配置。

举个简单的例子:

nginx 复制代码
http {
  limit_conn_zone $binary_remote_addr zone=ops:10m;

  # ...

  server {
    listen       80;
    server_name  www.javaboy.org;
    location / {
      limit_conn ops 1;  #这将指定一个地址只能同时存在一个连接。"one" 与上面的对应,也可以自定义命名
      limit_rate 300k;
  }

}

这里涉及到三个配置项:

  • limit_zone: 是针对每个 IP 定义一个存储 session 状态的容器,这个示例中定义了一个 10m 的容器,假设每个 session 的大小是 32bytes,那么可以处理 327680 个 session。
  • limit_conn ops 1:限制每个 IP 只能发起一个并发连接。
  • limit_rate 300k: 对每个连接限速 300k. 注意,这里是对连接限速,而不是对 IP 限速。如果一个 IP 允许两个并发连接,那么这个 IP 就是限速 limit_rate × 2

三 限制请求频率

Nginx 可以通过限制请求频率来防止服务器过载,最常见的场景就是登录请求,可以通过限制请求频率防止账号暴力破解。

Nginx 官方版本限制 IP 的连接和并发分别有两个模块:

  • limit_req_zone:用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 "leaky bucket"。
  • limit_req_conn 用来限制同一时间连接数,即并发限制。

举个栗子

nginx 复制代码
http {
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;

    server {
        listen 80;

        location / {
            limit_req zone=mylimit burst=5 nodelay;;
            proxy_pass http://javaboy.org;
        }
    }
}

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;

  • 第一个参数:$binary_remote_addr 表示通过 remote_addr 这个标识来做限制,"binary_" 的目的是缩写内存占用量,是限制同一客户端 ip 地址。
  • 第二个参数:zone=mylimit:10m 表示生成一个大小为 10M,名字为 mylimit 的内存区域,用来存储访问的频次信息。
  • 第三个参数:rate=1r/s 表示允许相同标识的客户端的访问频次,这里限制的是每秒 1 次,还可以有比如 30r/m 的。

limit_req zone=mylimit burst=5 nodelay;

  • 第一个参数:zone=one 设置使用哪个配置区域来做限制,与上面 limit_req_zone 里的 name 对应。
  • 第二个参数:burst=5,重点说明一下这个配置,burst 爆发的意思,这个配置的意思是设置一个大小为 5 的缓冲区,当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内。
  • 第三个参数:nodelay,如果设置,超过访问频次而且缓冲区也满了的时候就会直接返回 503,如果没有设置,则所有请求会等待排队。

四 防止目录遍历

在 Nginx 配置中设置 autoindex off 来防止目录遍历攻击。

这个一般是如果你要做文件服务器,根据自己的实际需求,有需要的话这个功能可以打开,否则将之关闭即可。

nginx 复制代码
location / {
    autoindex off;
}

五 隐藏 Nginx 版本号

攻击者如果能够确定服务器使用的 Nginx 版本,可能会利用这个信息来寻找和利用已知的漏洞进行攻击。因此,隐藏版本信息可以提高服务器的安全性,使攻击者难以通过版本信息推断出服务器可能存在的安全漏洞。

要隐藏 Nginx 版本号,有三个办法,一般来说我们使用第一种方式就可以了。

修改配置文件

在 Nginx 的配置文件中,在 http 块中添加以下配置:

nginx 复制代码
server_tokens off;

这样设置后,Nginx 将不会在错误页面上显示版本号。

配置完成之后,保存配置文件并重新加载 Nginx 以应用更改:

复制代码
nginx -t   # 测试配置文件是否正确
nginx -s reload   # 重新加载Nginx配置

这种方法可以隐藏错误页面上的版本信息,但可能无法完全隐藏所有响应头中的版本信息 。

修改 Nginx 源码

如果想要从根源上修改 Nginx 版本信息,需要重新编译 Nginx,步骤如下:

  • 修改 src/core/nginx.h 文件中的版本定义。
  • 修改 src/http/ngx_http_header_filter_module.c 文件中的服务器字符串。
  • 修改 src/http/ngx_http_special_response.c 文件中的错误页面底部信息。

修改完这些文件后,需要重新编译 Nginx。这样编译安装后,Nginx 的版本信息将被彻底修改 。

使用第三方模块

如果需要动态修改响应头中的版本信息,可以使用如 headers-more-nginx-module 模块。这个模块允许你动态地添加、修改或删除 Nginx 的响应头。通过这个模块,可以完全控制 Server 响应头的内容 。

选择哪种方法取决于你的具体需求和环境。

如果你只是想简单地隐藏版本信息,修改配置文件可能是最简单的方法。如果你需要更彻底地控制版本信息,可能需要考虑修改源码并重新编译 Nginx。

六 设置超时时间

设置 Nginx 的超时配置是非常重要的,因为它可以影响服务器的性能和资源的有效利用。

比较常见的超时配置有四个:

  1. keepalive_timeout :这个指令设置了与客户端的 keep-alive 连接超时时间。如果连接在指定时间内没有数据传输,Nginx 将关闭该连接。默认值通常是 75 秒。这个设置对于频繁访问的站点尤其重要,因为它减少了连接建立和断开的开销。
  2. client_body_timeout:这个指令指定了客户端与服务端建立连接后发送 request body 的超时时间。如果客户端在指定时间内没有发送任何内容,Nginx 返回 HTTP 408(Request Timed Out)。默认值通常是 60 秒。
  3. client_header_timeout:这个指令指定了客户端向服务端发送一个完整的 request header 的超时时间。如果在指定时间内没有发送一个完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)。默认值通常是 60 秒。
  4. send_timeout:这个指令设置了服务端向客户端传输数据的超时时间。如果在指定时间内客户端没有接收到任何数据,连接将被关闭。默认值通常是 60 秒。

针对这四个比较常见的超时配置,松哥这里也给大家一个配置案例。

keepalive_timeout

这个指令控制了客户端与服务器之间的连接保持活动状态的时间。这对于减少 TCP 连接的开销非常有用,特别是在高流量的网站上。

nginx 复制代码
http {
    keepalive_timeout 60s;

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            proxy_pass http://javaboy.org;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
        }
    }
}

在这个配置中,keepalive_timeout 被设置为 60 秒,意味着如果 60 秒内没有数据传输,连接将被关闭。

client_body_timeout

这个指令设置了客户端发送请求体到服务器的超时时间。

nginx 复制代码
http {
    client_body_timeout 10s;

    server {
        listen 80;
        server_name javaboy.org;

        location /upload {
            client_max_body_size 100M;
            client_body_timeout 30s;
        }
    }
}

在这个配置中,client_body_timeout 被设置为 10 秒,适用于上传大文件的场景,确保如果客户端在 30 秒内没有完成文件上传,请求将被终止。

client_header_timeout

这个指令控制了客户端发送完整的 HTTP 请求头到服务器的超时时间。

nginx 复制代码
http {
    client_header_timeout 5s;

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            proxy_pass http://javaboy.org;
        }
    }
}

在这个配置中,client_header_timeout 被设置为 5 秒,意味着如果客户端在 5 秒内没有发送完整的 HTTP 请求头,服务器将终止连接。

send_timeout

这个指令设置了服务器发送响应到客户端的超时时间。

nginx 复制代码
http {
    send_timeout 10s;

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            proxy_pass http://javaboy.org;
            proxy_read_timeout 10s;
        }
    }
}

在这个配置中,send_timeout 被设置为 10 秒,适用于后端服务响应慢的场景,确保如果后端服务在 10 秒内没有发送数据,客户端将收到超时响应。

七 仅允许域名访问

限制仅允许域名访问可以防止未授权的 IP 直接访问服务器,减少未备案域名解析到服务器 IP 导致的安全风险。

这个也有三种不同的配置方式,我们逐一来看。

使用两个 server

在 Nginx 配置文件中,你可以设置一个默认的 server 块,它将捕获所有不明确的域名请求,并返回 403 错误。然后,为特定的域名设置 server 块。

nginx 复制代码
server {
    listen 80 default_server;
    server_name _;
    return 403;
}

server {
    listen 80;
    server_name www.javaboy.org;
    location / {
        # 你的配置
    }
}

在这个配置中,第一个 server 块会拦截所有不明确域名的请求,并返回 403 错误。第二个 server 块则是为特定域名 www.javaboy.org 提供服务的配置。

这个配置有两点需要注意:

  1. 如果没有显式声明 default server 则第一个 server 会被隐式的设为 default server。
  2. server_name 中的 _;,并不是重点 __ 也可以, ___ 也可以。

使用 if 语句

还可以在特定的 server 块中使用 if 语句来检查 $host 变量,如果它不匹配你的域名,则返回 403 错误。

nginx 复制代码
server {
    listen 80;
    server_name javaboy.org;
    location / {
        if ($host != 'www.javaboy.org') {
            return 403;
        }
        # 你的配置
    }
}

这种方法允许你在特定域名的 server 块中直接控制访问权限,只有当 $host 变量与你的域名匹配时,才会允许访问。

直接禁止 IP

nginx 复制代码
http {
    server {
        listen 80;
        server_name www.javaboy.org;
        ...
    }
    
    server {
        listen 80;
        server_name www.itboyhub.com;
        ...
    }
    
    # 直接指定 ip server_name
    server {
        listen 80;
        server_name 11.11.11.11;
        return 403; # 403 forbidden
    }
    
}

这样配置后,只有通过指定的域名才能访问网站,直接通过 IP 地址访问将会受到限制。

八 限制 Nginx 请求方法

通过限制特定的 HTTP 请求方法,可以减少服务器受到自动化攻击的风险,并且可以防止某些类型的 Web 漏洞,如 SQL 注入或跨站脚本(XSS)攻击。

有两种配置方式,松哥来和大家逐一说明。

只允许 GET 和 POST

serverlocation 块中,使用 if 语句来检查请求方法,并返回 403 错误码以拒绝其他方法。

nginx 复制代码
server {
    listen 80;
    server_name javaboy.org;

    location / {
        if ($request_method !~* (GET|POST)) {
            return 403;
        }
        # 其他配置...
    }
}

这种方法会拒绝所有非 GET 和 POST 的请求方法。

使用 map 模块

对于更复杂的限制逻辑,可以使用 Nginx 的 map 模块来动态设置请求方法的限制。

nginx 复制代码
http {
    map $request_method $block_request {
        default 0;
        POST 1;
        PUT 1;
    }

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            if ($block_request) {
                return 403;
            }
            # 其他配置...
        }
    }
}

在这个例子中,所有 POST 和 PUT 请求都会被拒绝。

九 错误页面重定向

在 Nginx 中配置错误页面重定向,除了安全因素之外,还有很多好处,比如:

  1. 提升用户体验:通过提供更友好的错误页面,可以减少用户在遇到错误时的困惑和挫败感。
  2. 增强 SEO 效果:自定义错误页面可以帮助搜索引擎更好地理解网站结构,避免因错误页面导致的 SEO 问题。
  3. 维护品牌形象:错误页面是网站的一部分,通过自定义错误页面,可以保持品牌一致性,提升专业形象。
  4. 提供错误信息:自定义错误页面可以提供有用的错误信息或解决方案,帮助用户理解问题所在。

在 Nginx 配置文件中,可以使用 error_page 指令来定义特定错误代码的重定向页面。例如,将 404 错误重定向到自定义的 404 页面:

nginx 复制代码
server {
    listen 80;
    server_name javaboy.org;

    error_page 404 /404.html;
    location = /404.html {
        root /path/to/error/pages;
        internal;
    }
}

在这个配置中,当 Nginx 返回 404 错误时,它会显示位于 /path/to/error/pages/404.html 的自定义错误页面,而不是默认的错误页面。internal 指令确保这个页面只对 Nginx 内部请求可见,不会被外部直接访问 。

当然,上面这个配置也可以同时枚举多个错误状态码:

nginx 复制代码
error_page 500 502 503 504 /50x.html;
location = /50x.html {
    root /usr/share/nginx/html;
}

这个配置会将所有 500 系列错误重定向到 /50x.html,并显示位于 /usr/share/nginx/html/50x.html 的自定义错误页面 。

十 日志保留半年

保留 Nginx 日志半年的原因有很多,比如:

  1. 安全审计:日志文件可以用于安全审计,帮助分析和追踪潜在的攻击或异常行为。
  2. 故障排查:在系统出现故障时,日志文件是诊断问题的重要工具,可以帮助快速定位问题原因。
  3. 性能监控:通过分析日志,可以了解网站的访问情况和性能瓶颈,从而进行相应的优化。
  4. 合规性要求:某些行业法规可能要求保留一定期限的日志记录,以满足合规性检查。

要配置 Nginx 日志保留半年,通常需要使用 logrotate 工具来实现日志文件的定期轮换和压缩。

这个工具配置并不难,松哥给大家举个栗子。

/etc/logrotate.d/ 目录下创建一个名为 nginx 的配置文件,内容如下:

shell 复制代码
/var/log/nginx/*.log {
    daily
    rotate 180
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

这个配置会每天检查 Nginx 日志文件,并将它们保留 180 天(约 6 个月),然后自动压缩旧的日志文件。postrotate 部分的命令会在日志轮换后重新打开 Nginx 日志文件,以便继续记录新的日志信息。

通过这些配置,我们可以确保 Nginx 的日志文件被保留半年,同时旧的日志文件会被压缩以节省磁盘空间。

十一 设置缓冲区

Nginx 的缓冲区溢出攻击是一种常见的安全漏洞,它发生在程序试图向一个缓冲区写入超出其预分配大小的数据时。

这种攻击可能导致数据覆盖了相邻的内存区域,可能破坏程序的执行流程,甚至可以被恶意攻击者利用来执行恶意代码。

为了防止缓冲区溢出类攻击事件,可以设置客户端请求体、请求头和客户端最大请求体的缓冲区大小。

配置方式如下:

nginx 复制代码
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;

这四行配置含义如下:

  • client_body_buffer_size 1K;:这条指令设置了 Nginx 用来读取客户端请求体(比如 POST 请求中的数据)的缓冲区大小。在这个例子中,缓冲区大小被设置为 1KB。如果请求体的大小超过了这个缓冲区的大小,Nginx 会使用磁盘来暂存超出部分的数据。
  • client_header_buffer_size 1k;:这条指令定义了 Nginx 用来读取客户端 HTTP 请求头部的缓冲区大小。这里设置的大小是 1KB。如果请求头部的大小超过了这个缓冲区的大小,Nginx 会使用 large_client_header_buffers 定义的缓冲区。
  • client_max_body_size 1k;:这条指令限制了 Nginx 服务器愿意接收的最大请求体大小。如果客户端发送的请求体超过了这个大小(在这个例子中是 1KB),Nginx 将返回一个 413(Request Entity Too Large)错误。
  • large_client_header_buffers 2 1k;:这条指令定义了 Nginx 用于处理大于 client_header_buffer_size 指定大小的请求头的缓冲区数量和大小。这里配置了 2 个大小为 1KB 的缓冲区。当请求头的大小超过了 client_header_buffer_size 定义的缓冲区大小时,Nginx 会使用这两个额外的缓冲区来处理请求头。

这些配置对于防止缓冲区溢出攻击和处理大请求都是非常重要的。

十二 使用普通用户启动

在 Linux 系统中,只有 root 用户或者具有特定权限的用户才能绑定 1024 以下的端口,如 80 端口(HTTP)和 443 端口(HTTPS)。

如果 Nginx 以 root 用户运行,它将拥有过高的权限,这可能会带来安全风险。因此,为了最小化权限,通常会创建一个普通用户来运行 Nginx,以减少潜在的安全漏洞。

配置方式如下:

  1. 创建用户

首先,你需要创建一个普通用户和用户组,例如 nginx

复制代码
groupadd nginx
useradd -g nginx -d /usr/local/nginx nginx

这里创建了一个名为 nginx 的用户和组,并设置了用户的家目录。

  1. 授权访问

确保新用户有权访问 Nginx 的配置文件、日志文件和服务器文件。

复制代码
chown -R nginx:nginx /usr/local/nginx/

这条命令将 Nginx 目录及其所有子目录和文件的所有权更改为新创建的 nginx 用户和组。

  1. 配置Nginx

编辑 Nginx 配置文件(通常位于 /etc/nginx/nginx.conf),设置 user 指令以指定 Nginx 工作进程的用户。

复制代码
user nginx;

这行配置指定 Nginx 应该以 nginx 用户的身份运行。

  1. 设置权限

如果需要,可以使用 setcap 命令赋予 Nginx 监听 1024 以下端口的能力,而不需要以 root 用户运行。

复制代码
setcap cap_net_bind_service=+ep /usr/local/nginx/sbin/nginx

这个命令允许 Nginx 以普通用户身份绑定到 80 和 443 端口。

  1. 启动Nginx

使用普通用户启动 Nginx。

复制代码
su - nginx
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

这里首先切换到 nginx 用户,然后启动 Nginx 服务。

  1. 验证

检查 Nginx 是否以普通用户启动。

复制代码
ps -ef | grep nginx

这条命令将显示 Nginx 的进程信息,你可以验证它是否以 nginx 用户运行。

好啦,小伙伴们还有哪些 Nginx 安全配置建议?欢迎留言讨论。

相关推荐
christine-rr1 小时前
密码学基础——AES算法
网络·安全·密码学·加密
程序猿chen2 小时前
Vue.js组件化开发实战:从工程化到安全纵深设计
前端·vue.js·安全
遇见火星2 小时前
nginx或tengine服务器,配置HTTPS下使用WebSocket的线上环境实践!
服务器·websocket·nginx·https·tengine
小马爱打代码3 小时前
RPC与其他通信技术的区别,以及RPC的底层原理
网络·网络协议·rpc
用户27784491049934 小时前
Python使用OWASP ZAP进行Web应用安全测试
python·安全
花木凋零成兰4 小时前
驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接
java·安全·ssl
LuoYaFu5 小时前
文件上传漏洞原理学习
学习·安全·web安全
白山云北诗5 小时前
网络安全小知识课堂(十)
安全·web安全
ldq_sd6 小时前
Django 在同一域名下使用 NGINX 服务器运行 Django 和 WordPress
运维·服务器·nginx
Bruce_Liuxiaowei6 小时前
应急响应-进程排查
安全·web安全