Nginx 安全
约 8911 字大约 30 分钟
2025-10-31
始终保持 Nginx 最新
保持 Nginx 最新是安全性和性能的基础保障。
# 检查当前 Nginx 版本
nginx -v
# 查看最新稳定版
curl -s https://nginx.org/en/download.html | grep -o 'nginx-[0-9]*\.[0-9]*\.[0-9]*' | head -1包管理器更新
# Ubuntu/Debian
sudo apt update && sudo apt upgrade nginx
# CentOS/RHEL
sudo yum update nginx
# 检查更新后的版本
nginx -t && nginx -v源码编译更新(推荐最新版)
# 下载最新源码
wget https://nginx.org/download/nginx-1.24.0.tar.gz
tar zxvf nginx-1.24.0.tar.gz
cd nginx-1.24.0
# 配置(保留原有参数)
./configure --with-http_ssl_module --with-http_v2_module ... # 原有参数
# 编译安装
make
sudo make install
# 平滑重启
sudo nginx -s reload平滑升级
零停机更新,保留所有现有配置和模块,用户无感知升级。
准备工作
# 查看当前 Nginx 版本和编译参数
nginx -V
# 输出示例:
# nginx version: nginx/1.22.1
# built by gcc 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04)
# configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx ... --with-http_ssl_module --with-http_v2_module
# 备份当前 Nginx 二进制文件
sudo cp /usr/sbin/nginx /usr/sbin/nginx.backup.$(date +%Y%m%d)
# 备份配置目录
sudo cp -r /etc/nginx /etc/nginx.backup.$(date +%Y%m%d)获取并编译新版本
# 下载最新稳定版(以 1.24.0 为例)
cd /tmp
wget https://nginx.org/download/nginx-1.24.0.tar.gz
tar zxvf nginx-1.24.0.tar.gz
cd nginx-1.24.0
# 使用完全相同的编译参数(关键步骤!)
# 将之前 nginx -V 输出的 configure arguments 复制过来
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-threads \
--with-file-aio
# 编译(不安装)
make平滑升级操作
# 重要:先验证新版本兼容性
sudo /tmp/nginx-1.24.0/objs/nginx -t
# 预期输出:nginx: configuration file /etc/nginx/nginx.conf test is successful
# 备份旧二进制文件后替换
sudo mv /usr/sbin/nginx /usr/sbin/nginx.old
sudo cp /tmp/nginx-1.24.0/objs/nginx /usr/sbin/nginx
# 向主进程发送 USR2 信号,启动新版本工作进程
sudo kill -USR2 $(cat /var/run/nginx.pid)
# 此时新旧版本并存,旧进程变为老工人(old worker)
# 向旧主进程发送 WINCH 信号,优雅关闭旧工作进程
sudo kill -WINCH $(cat /var/run/nginx.pid.oldbin)
# 检查新进程是否正常运行
ps aux | grep nginx
# 应该看到新旧两组进程验证和回滚准备
# 验证新版本运行状态
curl -I http://localhost
nginx -v
# 监控错误日志
sudo tail -f /var/log/nginx/error.log
# 保持旧进程一段时间(便于快速回滚)
# 旧主进程仍在运行,只是没有工作进程关键信号说明
平滑升级信号流程
# 1. USR2 - 启动新主进程和工作进程
# 2. WINCH - 优雅关闭旧工作进程
# 3. QUIT - 完全关闭旧主进程(确认稳定后)
# 4. HUP - 重新加载配置(不影响升级)快速回滚方案
# 如果新版本有问题,立即回滚
sudo kill -HUP $(cat /var/run/nginx.pid.oldbin) # 重启旧工作进程
sudo kill -TERM $(cat /var/run/nginx.pid) # 关闭新进程
sudo mv /usr/sbin/nginx.old /usr/sbin/nginx # 恢复旧二进制文件平滑升级的并行运行机制
# 升级前的进程树
nginx: master process
├─ nginx: worker process
├─ nginx: worker process
└─ nginx: worker process
# 发送 USR2 信号后的进程树
nginx: master process (old) # 旧主进程
├─ nginx: worker process (old, closing) # 正在关闭的旧工作进程
└─ nginx: master process (new) # 新主进程(由旧进程fork产生)
├─ nginx: worker process (new) # 新工作进程
├─ nginx: worker process (new) # 新工作进程
└─ nginx: worker process (new) # 新工作进程Nginx 平滑升级时新旧版本可以同时运行而不会发生端口冲突,这是因为新主进程是通过旧进程 fork() 产生的子进程,直接继承了父进程的所有文件描述符,包括监听端口的 socket。
在操作系统内核层面,实际上只有一个监听 socket,两个进程共享同一个网络连接队列。这种机制确保了升级过程中:
- 零端口冲突 - 新旧进程使用相同的 socket 文件描述符
- 无缝切换 - 新工作进程逐步接管连接处理
- 零停机时间 - 用户完全感知不到升级过程
- 快速回滚 - 出现问题可立即恢复旧进程
这与手动启动两个独立 Nginx 实例完全不同,后者会因为各自创建独立的监听 socket 而导致端口冲突。平滑升级的核心优势就在于通过进程继承关系实现了资源的无缝交接。
非特权用户运行
在 Linux 系统中,最小特权原则(Principle of Least Privilege) 是安全配置的核心思想。
该原则强调:任何进程、用户或程序都 不应拥有超出完成任务所必需的权限。这样可以最大限度地降低潜在的攻击面和风险。
Nginx 在启动时确实需要使用 root 权限 来执行某些敏感操作,例如:
- 绑定特权端口(如 80、443)
- 访问系统级目录或配置文件
但在完成这些初始化操作后,Nginx 的工作进程(worker processes)就 不需要继续以 root 身份运行。 因此,出于安全考虑,应让这些进程以一个 非特权用户(如 nginx) 的身份运行。
简单来说:只有主进程(master process)以 root 运行,而工作进程应当以低权限用户运行。
配置方法
编辑 Nginx 主配置文件
# /etc/nginx/nginx.conf
user nginx;注
代表:
- 工作进程(worker processes)以
nginx这个用户身份运行; - 如果系统中有同名组(
nginx组),那工作进程默认也属于该组。
所以这里的 “nginx” 一词既是用户(user),也是用户组(group),通常二者同名。
你可以通过以下命令验证:
id nginx输出类似:
uid=997(nginx) gid=994(nginx) groups=994(nginx)说明:
- 用户名:nginx
- 用户组名:nginx
- UID/GID 为系统分配的标识号
这行配置指定了 Nginx 工作进程的运行用户与用户组。
如果不指定,默认可能是 nobody 或 www-data,取决于系统发行版。
调整网站目录权限
chown -R nginx:nginx /var/www/domain.com这一步确保 Nginx 进程对网页根目录具有读写权限(至少读权限),从而能正常访问静态资源。
仅仅修改进程名或用户显示名称不能提升安全性。真正的安全在于权限的隔离与控制。
如果你在容器环境(如 Docker)中运行 Nginx,同样建议使用非 root 用户镜像(例如官方镜像 nginx:alpine 可以通过参数指定运行用户)。
若必须使用 root(例如反向代理监听 80/443),可确保仅主进程持有 root 权限,而 worker 进程降权运行。
执行以下命令可验证 Nginx 的实际运行用户:
ps aux | grep nginx注
它其实是两条命令组合:
ps aux 用来列出当前系统中所有正在运行的进程信息。
a:显示所有用户的进程(不是只显示当前终端的)u:以用户为中心显示(会显示“进程所属用户”列)x:显示没有控制终端的进程(例如守护进程 daemon)
所以 ps aux 就是列出所有正在运行的进程的详细信息。
| grep nginx 管道符 | 表示“把前面命令的输出作为后面命令的输入”。 grep nginx 会从输出中过滤出包含 “nginx” 关键字的行。
因此整个命令意思是:
查看系统中所有包含 nginx 字样的进程信息
你会看到类似输出:
root 1013 0.0 0.1 123456 4321 ? Ss 10:20 0:00 nginx: master process /usr/sbin/nginx
nginx 1014 0.0 0.3 234567 5678 ? S 10:20 0:00 nginx: worker process| 列名 | 含义 |
|---|---|
root / nginx | 进程所属用户(owner) |
1013 / 1014 | 进程 ID(PID) |
nginx: master process | 命令或程序名称(即运行中的 Nginx 实例) |
/usr/sbin/nginx | 启动路径 |
| 其他 | CPU 占用、内存占用、启动时间、TTY 等信息 |
主进程 root + 工作进程降权运行步骤
实现如下运行状态:
root 1234 ... nginx: master process /usr/sbin/nginx
nginx 1235 ... nginx: worker process即:
- 只有主进程是 root(用于监听特权端口 80/443);
- 所有工作进程(处理请求的部分)以普通用户
nginx运行。
步骤总览
- 确认 Nginx 主配置文件路径
- 创建非特权用户与组
- 修改配置文件中的运行用户
- 修改网站目录权限
- 验证并重启服务
- 检查运行状态
确认配置文件与安装路径
不同安装方式配置路径不同:
| 安装方式 | 主配置文件路径 | 二进制路径 |
|---|---|---|
| 官方包 / Ubuntu | /etc/nginx/nginx.conf | /usr/sbin/nginx |
| CentOS / RHEL | /etc/nginx/nginx.conf | /usr/sbin/nginx |
| 宝塔面板 / aaPanel | /www/server/nginx/conf/nginx.conf | /www/server/nginx/sbin/nginx |
| 自编译安装 | 取决于 --prefix 参数 | prefix/sbin/nginx |
执行以下命令可自动探测:
nginx -t会输出类似:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful记下那条 configuration file 的路径。
创建非特权用户和用户组
如果系统中还没有 nginx 用户,创建一个专用的:
sudo groupadd -r nginx
sudo useradd -r -g nginx -s /sbin/nologin -d /var/www nginx说明:
-r表示创建系统用户(无登录权限);-g nginx指定用户组;-s /sbin/nologin禁止直接登录;-d /var/www作为默认主目录。
编辑 Nginx 主配置文件
找到 nginx.conf 文件(上一步查到的路径),打开:
sudo vim /etc/nginx/nginx.conf(或替换为你的实际路径,如 /www/server/nginx/conf/nginx.conf)
在顶部区域找到或添加:
user nginx;注意:
- 如果该行是注释(
#user ...),去掉#; - 如果系统中面板使用了别的用户名(如
www或www-data),可按需改为那个。
调整网站文件权限
给该用户访问网站目录的权限(只需可读即可):
sudo chown -R nginx:nginx /var/www/html或(如果是面板环境):
sudo chown -R nginx:nginx /www/wwwroot若只想赋予读权限而不修改文件所有权,可:
sudo chmod -R 755 /www/wwwroot检查配置与重启服务
sudo nginx -t若输出:
nginx: configuration file ... is ok
nginx: test is successful说明一切正常。
然后重启 Nginx:
系统服务方式:
sudo systemctl restart nginx面板安装方式:
/www/server/nginx/sbin/nginx -s reload验证运行用户
ps aux | grep nginx如果一切配置正确,应看到类似输出:
root 1234 ... nginx: master process /usr/sbin/nginx
nginx 1235 ... nginx: worker process表示主进程为 root、工作进程为 nginx 用户,降权运行成功。
为什么这样设计?
root 进程职责
- 启动时绑定 80/443 等特权端口
- 管理工作进程(worker)生命周期
worker 进程职责
- 实际处理 HTTP 请求
- 不需要访问系统资源,因此降权运行更安全
若 Nginx 被攻破,攻击者只能获得低权限账户,无法直接操作系统文件或执行特权命令。
禁用不必要的模块
在生产环境中,禁用所有不需要的 Nginx 模块 是一项关键的安全加固措施。 这是因为每一个启用的模块都会:
- 扩大攻击面(潜在漏洞增多)
- 增加内存占用与启动时间
- 引入不必要的阻塞操作,降低性能
因此,遵循 最小功能原则(Principle of Least Functionality),仅保留业务所需的模块,禁用或卸载多余组件,是构建高安全性 Web 服务器的重要一步。
实现方式
Nginx 模块分为两类:
- 静态模块(static):编译时确定,写入二进制文件;
- 动态模块(shared / dynamic):运行时通过
load_module指令加载。
不同类型的模块禁用方式不同
在安装阶段禁用不必要模块(静态模块)
在编译安装 Nginx 时,可以通过 ./configure 选项关闭默认启用但无用的模块:
./configure \
--without-http_autoindex_module \
--without-http_ssi_module \
--without-http_userid_module \
--without-http_auth_basic_module示例说明:
--without-http_autoindex_module:关闭目录自动索引功能(防止暴露文件列表)
--without-http_ssi_module:关闭服务器端包含(SSI)功能
--without-http_userid_module:关闭用户追踪 cookie 模块
--without-http_auth_basic_module:关闭基础认证模块(如未使用 basic auth)
编译后可使用以下命令查看已启用模块:
nginx -V它会显示类似:
--with-http_ssl_module --without-http_autoindex_module ...禁用动态模块(load_module)
如果是通过包管理器或控制面板(如宝塔)安装的 Nginx,
很多模块是以 动态共享库(.so) 的形式加载的。
打开 modules.conf 或主配置文件中的加载区域,通常位于:
/etc/nginx/modules.conf
/www/server/nginx/conf/modules.conf注释掉不使用的模块即可:
## load_module /usr/share/nginx/modules/ndk_http_module.so;
## load_module /usr/share/nginx/modules/ngx_http_auth_pam_module.so;
## load_module /usr/share/nginx/modules/ngx_http_cache_purge_module.so;
## load_module /usr/share/nginx/modules/ngx_http_dav_ext_module.so;
load_module /usr/share/nginx/modules/ngx_http_echo_module.so;
## load_module /usr/share/nginx/modules/ngx_http_fancyindex_module.so;
load_module /usr/share/nginx/modules/ngx_http_geoip_module.so;
load_module /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so;
## load_module /usr/share/nginx/modules/ngx_http_image_filter_module.so;
## load_module /usr/share/nginx/modules/ngx_http_lua_module.so;
load_module /usr/share/nginx/modules/ngx_http_perl_module.so;
## load_module /usr/share/nginx/modules/ngx_mail_module.so;
## load_module /usr/share/nginx/modules/ngx_nchan_module.so;
## load_module /usr/share/nginx/modules/ngx_stream_module.so;注意:注释前请确认模块确实未被任何配置使用,否则会导致启动失败。
修改后执行:
nginx -t
nginx -s reload确保配置文件语法正确并成功重载。
性能与安全提示
避免使用低质量第三方模块
很多第三方模块存在性能隐患(阻塞 I/O、线程不安全),一旦部署在高并发环境下,可能破坏 Nginx 的异步事件模型,导致请求延迟、CPU 飙升甚至崩溃。
仅选择高质量模块来源
建议使用:
- 官方维护模块;
- 或来自 OpenResty、Nginx 官方 GitHub、Nginx, Inc. 认证模块库 的第三方模块。
定期审查加载的模块列表
nginx -V 2>&1 | grep module确认没有意外启用的模块。
保护敏感资源
在 Web 应用部署中,一些隐藏目录和文件包含关键数据或源代码,例如:
.git、.svn、.hg等版本控制目录.env、.htpasswd、.htaccess等配置文件- 临时备份文件或敏感脚本
这些敏感资源如果被公开访问,攻击者可能:
- 下载源代码,寻找漏洞
- 获取数据库或 API 密钥
- 重新构建整个站点并进行滥用
因此,隐藏的目录和文件永远不应该直接暴露在 Web 访问路径下。
特别注意:即使你认为目录是“隐藏”的(以点开头),Nginx 默认也不会自动阻止访问。
拒绝访问特定目录
# 拒绝访问 .git 目录
location ~ /\.git {
deny all;
}
# 或者拒绝 .svn
location ~ /\.svn {
deny all;
}使用正则匹配多个敏感类型
# 拒绝访问 .git、.svn、.htaccess 文件
location ~* ^.*(\.(?:git|svn|htaccess))$ {
return 403;
}阻止所有以 . 开头的隐藏文件/目录(保留 .well-known)
location ~ /\.(?!well-known/) {
deny all;
}.well-known 是 Let's Encrypt 等证书自动验证需要访问的目录,因此要例外允许。
限制敏感文件类型
location ~* \.(bak|sql|tar|zip|old|backup)$ {
deny all;
}限制应用配置文件
location ~* \.env$ {
deny all;
}隔离上传目录
location /uploads/ {
# 禁止直接执行脚本文件
location ~* \.php$ {
deny all;
}
}隐藏版本号
信息泄露风险:Nginx 默认会在 HTTP 响应头 Server 中显示版本号,例如:
Server: nginx/1.26.3攻击者可以利用已知漏洞针对特定版本发起攻击。
安全神话:尽管隐藏版本号无法真正防止漏洞利用(安全性依赖补丁和配置),但可以降低被自动化扫描器直接识别的风险,减少攻击面。
官方文档指出,不要期望仅通过隐藏版本号获得真正安全:“安全通过默默无闻获得”是神话。隐藏版本号只是一种降低信息暴露的辅助措施,不能替代补丁和安全配置。
同时,ServerTokens 不能设置得过低,否则可能影响调试与互操作性。
在 nginx.conf 中关闭版本显示
http {
server_tokens off;
}server_tokens off; 会将 Server 响应头仅显示 nginx,不显示具体版本号:
Server: nginx可以放在 http 块,也可以放在 server 或 location 块,但推荐全局配置在 http 块。
完全自定义 Server 头
如果希望进一步控制响应头,可以使用 more_set_headers(需要 headers-more 模块):
# 在 server 或 location 块中
more_set_headers "Server: MyCustomServer";效果:把默认的 nginx 服务器标识替换成自定义内容。
注意:这并不会增加真正安全性,只是进一步模糊服务器信息。
标准 Nginx(官方 Windows/Linux 二进制)默认不带 headers-more,使用 more_set_headers 会报错
编译带 more_set_headers 模块的 Nginx:
查看当前 Nginx 编译信息
nginx -V输出示例:
nginx version: nginx/1.26.3
built by gcc 12.2.0 (Debian 12.2.0-14)
configure arguments: --user=www --group=www --prefix=/www/server/nginx --with-http_ssl_module --with-http_v2_module ...- 作用:获取现有编译选项,确保重新编译时保持原来的模块和路径。
- 保存下
configure arguments,后续会在新编译中复用。
下载源码与模块
- 下载 Nginx 源码(和你当前版本一致)
wget http://nginx.org/download/nginx-1.26.3.tar.gz
tar zxvf nginx-1.26.3.tar.gz
cd nginx-1.26.3- 下载
headers-more模块
git clone https://github.com/openresty/headers-more-nginx-module.git配置编译参数
复用原来的 configure 参数,并添加模块:
./configure \
--user=www \
--group=www \
--prefix=/www/server/nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_stub_status_module \
--add-module=/path/to/headers-more-nginx-module注意:
- 保持原来的参数,避免丢失已有功能
--add-module指向 headers-more 模块的路径
编译与安装
make
make installmake install 会覆盖 /www/server/nginx(或你之前的 prefix),也可以指定 --prefix 到新路径并测试。
验证模块加载
nginx -V检查输出中是否有:
--add-module=/path/to/headers-more-nginx-module隐藏上游代理标头
当 Nginx 作为反向代理或负载均衡器使用时,请求通常会被转发到上游服务器,例如:
- PHP-FPM、Node.js、Tomcat、ASP.NET
- 后端 CMS(Drupal、WordPress 等)
上游服务器可能会在 HTTP 响应头中包含敏感信息,例如:
X-Powered-By→ 告诉攻击者你在用 PHP、ASP.NET 等X-AspNet-Version/X-AspNetMvc-Version→ ASP.NET 版本X-Drupal-Cache→ Drupal 缓存信息
这些信息会暴露后台技术栈和版本号,增加攻击面。
虽然隐藏这些标头无法修复漏洞,但可以减少自动化扫描器直接识别的可能性,提高安全性。
在 Nginx 的 server 或 location 块中使用 proxy_hide_header:
# 隐藏常见上游标头
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNetMvc-Version;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Drupal-Cache;作用:Nginx 将从上游接收到的这些响应头去掉,不会返回给客户端。
使用场景:
- 反向代理 PHP-FPM
- 代理 Node.js / Express / ASP.NET 服务
- 代理 CMS 系统(WordPress、Drupal、Joomla 等)
强制所有连接通过 TLS
- 加密传输:保护用户数据免受嗅探
- 身份验证:确保客户端连接的是正确的服务器
- 防止混合内容:HTTPS 页面中不要加载 HTTP 子资源
- 强制 HSTS:增强浏览器安全策略
即便是非敏感页面,也应尽量使用 HTTPS,现代浏览器会警告或阻止 HTTP 内容。
全站强制 HTTPS(推荐)
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
# HTTPS 主站
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
# 其他安全配置...
}只对敏感页面强制 HTTPS(登录、注册等)
server {
listen 80;
server_name example.com;
location ^~ /login {
return 301 https://example.com$request_uri;
}
location ^~ /register {
return 301 https://example.com$request_uri;
}
}可选:直接关闭 80 端口
- 如果确定所有用户都通过 HTTPS 访问,可在防火墙或 Nginx 配置中关闭 HTTP(端口 80)
- 简化管理,避免 HTTP 重定向配置
核心建议
全站 HTTPS 最安全,尽量避免仅对部分路径开启
启用 HSTS:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;使用免费证书:Let's Encrypt 提供免费且自动化的 TLS 证书
注
免费证书 vs 付费证书
信任级别
- 免费证书(DV):只验证域名所有权
- 付费证书(OV/EV):验证公司/组织身份,浏览器可能显示公司名称,附带赔偿保障
有效期与续期
- 免费:90 天,需要自动续期
- 付费:1~2 年,可人工续期,附带技术支持
支持与功能
- 免费:社区支持,功能有限
- 付费:官方售后、更多域名支持、通配符证书、多域名证书
总结
- 加密效果一样
- 差别在于 身份验证严格程度、售后服务、企业可信度
- 小型网站、博客:免费证书够用
- 企业、金融、支付网站:付费证书更适合
使用受支持的最新 OpenSSL 版本
OpenSSL 是一个开源的 加密库,提供 TLS/SSL 协议支持、加密算法、证书管理等功能
Nginx 使用 OpenSSL 来实现 HTTPS(加密传输)
它负责:
- 数据加密/解密(对称加密、非对称加密)
- 数字证书验证(X.509)
- 安全通信协议(TLS/SSL)
简单理解:OpenSSL 就是 Nginx 的“加密引擎”,没有它,HTTPS 就不能工作。
为什么要使用最新受支持版本?
旧版本 OpenSSL 可能存在 已知漏洞
最新版本提供:
- 安全修复
- 新增算法支持
- 性能优化
只使用仍受官方支持的版本(LTS)才能保证安全性
检查系统自带 OpenSSL 版本:
openssl version如果过旧:
- 在 Linux 系统上可以通过包管理器升级
- 或者 源码编译 OpenSSL,并在 Nginx 编译时指定:
./configure --with-openssl=/path/to/openssl配置 Nginx 使用最新 OpenSSL 版本,实现安全的 TLS 支持
总结
- OpenSSL 是 Nginx HTTPS 的加密核心
- 使用最新受支持版本能避免漏洞和协议缺陷
- 对安全敏感的网站,这一步非常关键
TLS 使用最少 2048 位私钥
当你在网站上启用 HTTPS(也就是浏览器地址栏显示锁)时,网站需要一个 私钥(Private Key) 来建立安全连接。
- 私钥是一种秘密密码,它是网站“身份”和加密通信的核心。
- 浏览器和服务器通过这个私钥来进行 TLS/SSL 握手,确认网站身份,并生成加密通信的密钥。
简单比喻:
- 网站的私钥 = 网站的身份证 + 安全钥匙
- 浏览器通过它确认“你访问的网站是真正的网站”,并建立加密通道
作用:
HTTPS(TLS/SSL)连接:浏览器访问 https://example.com
证书签名:私钥配合证书(Certificate)一起使用,保证加密和身份验证
SSL/TLS 握手:客户端和服务器协商加密算法、生成对称密钥、验证身份
为什么要使用 2048 位?
密钥长度决定加密强度:
- 太短 → 容易被破解
- 太长 → 安全性增加,但网站响应慢,CPU 消耗大,用户体验差
当前推荐:RSA 2048 位足够商业用途,兼顾安全和性能
使用 4096 位主要是为了 SSL Labs 测试满分或心理安全感,实际提升不大
**ECC(椭圆曲线加密)**密钥更短也能提供同等安全性,速度更快
总结:
- RSA 2048 位 = 足够安全 + 性能合理
- ECC / x25519 = 更高性能、更短密钥、同等安全
- 4096 位 RSA = 可选,不必要
生成 RSA 2048 位私钥
openssl genrsa -out domain.com.key 2048生成的 domain.com.key 就是你的私钥,用于 HTTPS 和证书签名
使用 Let's Encrypt 生成证书(RSA 2048)
certbot certonly -d domain.com -d www.domain.com --rsa-key-size 2048生成 ECC(ECDSA)私钥(更短、更快)
openssl ecparam -out domain.com.key -name prime256v1 -genkey
openssl req -new -key domain.com.key -out domain.com.csr -sha256使用 x25519 曲线(TLS 1.3 推荐)
openssl genpkey -algorithm x25519 -out private.key这些操作最终都会生成私钥和证书,用于 HTTPS 加密和身份验证。
| 概念 | 作用 |
|---|---|
| 私钥(Private Key) | 网站的身份证 + 加密钥匙,用于安全通信 |
| 证书(Certificate) | 公钥 + 网站信息,由 CA 签发,验证网站身份 |
| RSA 2048 位 | 足够安全的加密长度,兼顾性能 |
| ECC / x25519 | 更现代的加密方式,密钥短、效率高 |
| SSL/TLS 握手 | 浏览器和服务器建立安全连接的过程 |
总结
- 私钥是 HTTPS 的核心,必须安全存储
- 2048 位 RSA 是推荐值,ECC 可选
- 过短 → 不安全
- 过长 → 性能差,用户体验下降
- 私钥 + 证书 = 浏览器能安全访问你的网站
小结一句话:私钥决定你的网站加密“强度”和身份可信度,2048 位足够安全,ECC 更快更现代,保证用户访问你的网站是安全的。
仅保留 TLS 1.3 和 TLS 1.2
当用户访问你的网站时,浏览器和服务器需要建立 加密连接,保证数据传输安全,这就是 TLS/SSL。
TLS/SSL 协议 = 网站和浏览器通信的加密规则
不同版本的 TLS/SSL 协议加密强度不同:
- 旧版本(SSLv2/SSLv3/TLS1.0/TLS1.1)有安全漏洞
- 新版本(TLS1.2/TLS1.3)更安全、更快
简单比喻:
- TLS 协议 = 浏览器和网站“说话”的语言
- 老版本 = “破旧方言”,容易被监听或破解
- 新版本 = “现代语言”,安全可靠
作用
- HTTPS 网站的 加密通信
- 浏览器和服务器的 TLS 握手
- 控制网站支持哪些加密协议版本和密码算法
这就是控制网站“能用哪种安全加密方式和通信规则”。
为什么只保留 TLS 1.2 和 TLS 1.3?
| 版本 | 安全性 | 说明 |
|---|---|---|
| SSLv2 / SSLv3 | 不安全 | 很容易被攻击,已经废弃 |
| TLS 1.0 / TLS 1.1 | 不安全 | 使用过时算法,浏览器已不支持 |
| TLS 1.2 | 安全 | 当前最常用,支持现代加密算法,需要避免 CBC 式弱密码 |
| TLS 1.3 | 更安全 & 快 | 新协议,简化握手,删除过时算法,速度更快 |
TLS1.2:必须仔细配置密码套件,确保使用现代安全算法
TLS1.3:自动使用最安全密码,握手更快,消除了 TLS1.2 的一些复杂问题
浏览器访问网站时,如果服务器只支持安全版本,SSL/TLS 握手更安全
启用 TLS 1.3 和 TLS 1.2
ssl_protocols TLSv1.3 TLSv1.2;只启用 TLS 1.2(兼容性需求)
ssl_protocols TLSv1.2;错误示例(不要用 TLS1.0 / 1.1)
ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1; # 不推荐
ssl_protocols TLSv1.2 TLSv1.1; # 不推荐正确配置可以让 SSL Labs 得到 100% 分,错误配置会降低分数,说明安全性不足。
总结
- TLS = HTTPS 的加密规则
- TLS1.2 + TLS1.3 = 安全可靠
- TLS1.0/1.1/SSL = 不安全,必须禁用
- 配置示例就是告诉 Nginx:“我只允许安全的 TLS 版本,让用户通信更安全”
只让浏览器用最新、最安全的方式和网站说话,旧的破语言全部禁止。
如何在 SSL Labs 测试网站安全性
- 打开浏览器,访问:https://www.ssllabs.com/ssltest/
- 在 “Hostname” 输入你的网站域名(如
example.com) - 点击 “Submit”
- 等待测试完成(通常几分钟)
- 查看结果:
- Protocol Support:确认只启用 TLS1.2 / TLS1.3
- Key Exchange:检查 RSA / ECC 私钥长度
- Grade:A+ 表示配置非常安全
小技巧:
SSL Labs 会检测协议版本、密钥长度、证书链、加密算法等
调整 Nginx 配置后可以重新测试,直到达到理想分数
强密码配置
强密码套件是什么?
- 密码套件(Cipher Suite):TLS/HTTPS 里定义的加密算法组合 包括:
- 密钥交换算法(Key Exchange) – 生成会话密钥
- 加密算法(Encryption) – 用于加密通信数据
- 消息认证算法(MAC) – 确保数据未被篡改
- 强密码套件 = 使用现代加密算法 + 前向保密 + 安全哈希
- 弱密码套件 = 使用过时算法(如 RC4、DES、MD5)或不支持前向保密,容易被破解
简单理解:密码套件就像通信的锁和钥匙组合,强密码套件就是最坚固的锁。
为什么要使用强密码套件?
- 防止信息被窃取 弱密码可能被攻击者解密,获取用户数据或会话信息
- 防止回放和破解攻击
- 没有前向保密的密钥一旦泄露,以前的通信也能被解密
- CBC、RSA 弱密码容易受到 ROBOT、POODLE 等攻击
- 提高 SSL Labs 测试分数
- 使用强密码 + TLS1.2/1.3 → A+
- 弱密码或兼容旧 TLS → 分数下降
- 兼顾速度和安全性
- ECDHE 套件比 DHE 更快
- TLS1.3 本身只支持安全套件,Nginx 默认使用最安全的
Nginx 推荐强密码套件配置
# TLS1.3 + TLS1.2
ssl_protocols TLSv1.3 TLSv1.2;
# TLS密码套件(强套件)
ssl_ciphers "TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:
DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:
ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:
ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
DHE-RSA-AES128-GCM-SHA256";
ssl_prefer_server_ciphers on; # 使用服务器推荐顺序- TLS1.3 密码固定,Nginx 无法自定义,但默认安全
- TLS1.2 需要手动配置套件顺序,ECDHE 放在前面保证速度和前向保密
- 禁用弱密码套件:DES、3DES、RC4、MD5、SHA1、RSA(无前向保密)
检查服务器支持的密码套件
# 查看支持的密码套件
openssl ciphers -s -v
# 只查看支持 ECDHE 的套件
openssl ciphers -s -v ECDHE
# 只查看支持 DHE 的套件
openssl ciphers -s -v DHE总结:
- 强密码 = 现代算法 + 前向保密 + 安全哈希
- TLS1.3 默认安全;TLS1.2 需配置密码套件
- 弱密码套件(DES/3DES/RC4/MD5/SHA1/RSA)不要使用
- 使用 SSL Labs 测试,确保 A+ 分数
- ECDHE/DHE 前置,提高性能和安全性
使用更安全的 ECDH 曲线
理由:
- 椭圆曲线(ECC)决定了 ECDHE 密钥交换的安全性。
- SafeCurves 网站评估了各种曲线的安全性。
- 对 TLS 1.2/1.3,推荐:
- x25519(最安全,但兼容性稍差)
- prime256v1 / P-256(最常用,兼容性好)
- secp384r1 / P-384(可选,增加安全性但增加计算成本)
- 避免使用安全性不足的小曲线,如 secp112r1、secp128r1 等。
配置示例:
# TLS 1.2 和 1.3 的安全曲线顺序
ssl_ecdh_curve X25519:secp521r1:secp384r1:prime256v1;推荐顺序:x25519 > secp521r1 > secp384r1 > prime256v1
TLS 1.3 默认曲线 x25519,无需 Nginx 显式设置,但可以指定顺序以提高兼容性。
使用具有完全前向保密的强密钥交换
理由:
- 前向保密(Forward Secrecy, FS)保证会话密钥泄露不会影响历史通信。
- ECDHE(椭圆曲线 DH 临时密钥)比 DHE 快且安全。
- DHE 默认使用 1024 位参数弱,建议至少 2048 位。
- TLS 1.3 默认启用 ECDHE,DHE 可选。
DH 参数生成:
# 2048 位 DH
openssl dhparam -out /etc/nginx/ssl/dhparam_2048.pem 2048
# 4096 位 DH
openssl dhparam -out /etc/nginx/ssl/dhparam_4096.pem 4096Nginx 配置:
ssl_dhparam /etc/nginx/ssl/dhparam_2048.pem;TLS 1.3 + ECDHE 不需要设置 ssl_dhparam
仅使用 DHE 时才需要 DH 参数
防止 TLS 1.3 0-RTT 重放攻击
理由:
- 0-RTT 可提升性能,但可能导致重放攻击。
- 默认 TLS 1.3 未启用 0-RTT,初学者应禁用。
- 若启用,需在应用层检测
$ssl_early_data。
配置示例:
server {
ssl_protocols TLSv1.2 TLSv1.3;
# 禁用或开启 0-RTT
ssl_early_data off;
location / {
proxy_pass http://backend;
proxy_set_header Early-Data $ssl_early_data;
}
}测试 0-RTT:
openssl s_client -connect example.com:443 -tls1_3 -sess_out session.pem
openssl s_client -connect example.com:443 -tls1_3 -sess_in session.pem -early_data req.in防御 BEAST 攻击
理由:
BEAST 攻击依赖 TLS 1.0/1.1 和 CBC 分组密码。
防御措施:
- 优先服务器密码顺序
- 禁用 TLS 1.0
- 仅使用 TLS 1.2+ 强密码套件
配置示例:
ssl_prefer_server_ciphers on;缓解 CRIME / BREACH 攻击
理由:
- CRIME 利用 TLS 压缩
- BREACH 利用 HTTP 压缩的敏感响应
措施:
- 禁用动态 TLS 压缩(Nginx 1.3.2+ 默认禁用 TLS 压缩)
- HTTP 压缩仅对非敏感内容使用
- 对静态资源使用
gzip_static
配置示例:
# 禁用动态 HTTP 压缩
gzip off;
# 对特定静态资源启用压缩
location ^~ /assets/ {
gzip_static on;
}静态文件 gzip 可以提高性能且安全
动态压缩敏感响应需谨慎
HTTP 严格传输安全 (HSTS)
理由:
- 告诉浏览器:只允许 HTTPS 连接,防止 MITM、降级攻击、明文 cookie 泄露。
max-age建议 12~24 个月(31536000~63072000 秒)。- 推荐启用
includeSubDomains,可选preload。 - 启用 HSTS 前确保全站 HTTPS 可用。
配置示例:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;若要加入 HSTS 预加载列表,请使用 preload 并满足列表要求
内容安全策略 (CSP)
理由:
- 限制资源加载来源,减少 XSS 风险。
- 可以阻止内联脚本和
eval,降低攻击面。 - 需与开发团队协作更新应用,确保策略不破坏功能。
配置示例:
add_header Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';" always;默认拒绝所有内容,通过白名单放行必要资源
Referer 控制 (Referrer-Policy)
理由:控制请求中发送的 Referer 信息,减少敏感信息泄露。
示例:
add_header Referrer-Policy "no-referrer" always;点击劫持保护 (X-Frame-Options)
理由:防止网站被嵌入 iframe,避免 UI 劫持攻击。
示例:
add_header X-Frame-Options "SAMEORIGIN" always;防止 XSS (X-XSS-Protection)
理由:启用浏览器内置的 XSS 过滤器(现代浏览器)。
示例:
add_header X-XSS-Protection "1; mode=block" always;防止 MIME 嗅探 (X-Content-Type-Options)
理由:阻止浏览器基于内容猜测 MIME 类型,防止 MIME 注入攻击。
示例:
add_header X-Content-Type-Options "nosniff" always;拒绝不必要的浏览器功能 (Feature-Policy / Permissions-Policy)
理由:禁止第三方或旧 API 滥用,提高隐私安全。
示例:
add_header Feature-Policy "geolocation 'none'; midi 'none'; notifications 'none'; push 'none'; sync-xhr '限制不安全 HTTP 方法
理由:公共服务器仅需支持 GET、POST、HEAD,其他方法增加攻击面。
示例:
add_header Allow "GET, POST, HEAD" always;
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 405;
}防止缓存敏感数据
理由:避免浏览器缓存敏感信息,保证安全性。
示例:
location /api {
expires 0;
add_header Cache-Control "no-cache, no-store, private, must-revalidate, max-age=0, no-transform";
}控制缓冲区大小
理由:缓冲区溢出攻击是通过将数据写入缓冲区并超出该缓冲区的边界并覆盖进程的内存片段来实现的。为了防止这种情况在 Nginx 中,我们可以为所有客户端设置缓冲区大小限制。
示例:
client_body_buffer_size 100k;
client_header_buffer_size 1k;
client_max_body_size 100k;
large_client_header_buffers 2 1k;缓解慢速 HTTP DoS 攻击(Slowloris)
理由:避免攻击者通过慢速连接耗尽服务器资源。
示例:
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 5s 5s;
send_timeout 10s;完整安全配置示例
涵盖:TLS、安全头、缓冲区限制、DoS 防护等,可直接用在生产环境。你可以在你的 server 或 http 块里使用它。
# -------------------------------
# 基础安全设置
# -------------------------------
server_tokens off; # 隐藏 Nginx 版本号
more_clear_headers Server; # 隐藏上游代理标头(需要 ngx_headers_more 模块)
# -------------------------------
# 强制 HTTPS + TLS 配置
# -------------------------------
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/certs/example.crt;
ssl_certificate_key /etc/ssl/private/example.key;
# 使用最新 OpenSSL,最少 2048 位私钥
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:...
# 使用强密钥交换 + ECDH 曲线
ssl_ecdh_curve X25519:P-521:P-384:P-256;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off; # 防止 TLS 1.3 0-RTT 重放攻击
ssl_dhparam /etc/ssl/certs/dhparam.pem; # 生成 2048+ 位 DH 参数文件
# -------------------------------
# HTTP 严格传输安全 (HSTS)
# -------------------------------
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# -------------------------------
# 内容安全策略 (CSP)
# -------------------------------
add_header Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';" always;
# -------------------------------
# Referer 控制
# -------------------------------
add_header Referrer-Policy "no-referrer" always;
# -------------------------------
# 点击劫持保护
# -------------------------------
add_header X-Frame-Options "SAMEORIGIN" always;
# -------------------------------
# 防 XSS
# -------------------------------
add_header X-XSS-Protection "1; mode=block" always;
# -------------------------------
# 防 MIME 嗅探
# -------------------------------
add_header X-Content-Type-Options "nosniff" always;
# -------------------------------
# 功能控制
# -------------------------------
add_header Feature-Policy "geolocation 'none'; midi 'none'; notifications 'none'; push 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; vibrate 'none'; fullscreen 'none'; payment 'none'; usb 'none';" always;
# -------------------------------
# 限制不安全 HTTP 方法
# -------------------------------
add_header Allow "GET, POST, HEAD" always;
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 405;
}
# -------------------------------
# 防止缓存敏感数据
# -------------------------------
location /api {
expires 0;
add_header Cache-Control "no-cache, no-store, private, must-revalidate, max-age=0, no-transform";
}
# -------------------------------
# 控制缓冲区大小
# -------------------------------
client_body_buffer_size 100k;
client_header_buffer_size 1k;
client_max_body_size 100k;
large_client_header_buffers 2 1k;
# -------------------------------
# 缓解慢速 HTTP DoS 攻击
# -------------------------------
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 5s 5s;
send_timeout 10s;
# -------------------------------
# 重定向 HTTP 到 HTTPS
# -------------------------------
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}- DH 参数:
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048 - 隐藏上游代理:
more_clear_headers Server;需要ngx_headers_more模块。 - TLS 配置:仅启用 TLS 1.2+,优先使用 ECDHE 密钥交换,提供前向保密。
- CSP / HSTS:生产环境可根据实际需求调整策略严格度。
- HTTP 方法限制:GET/POST/HEAD 为最小必要集,可扩展。