目录
一、Nginx 概述
在互联网技术蓬勃发展的当下,Web 服务的安全性和可靠性至关重要。Nginx 作为一款高性能的 Web 服务器和反向代理工具,凭借其轻量级架构、卓越的高并发处理能力以及灵活的模块化设计,在全球 Web 服务器市场中占据近三分之一的份额,成为众多企业和开发者的首选。然而,网络攻击手段层出不穷,如 DDoS 攻击、SQL 注入、恶意爬虫等,同时数据隐私保护法规日益严格,如 GDPR、等保 2.0 等,这使得掌握 Nginx 的安全防护策略与 HTTPS 部署能力,成为运维工程师和开发者不可或缺的技能。
二、Nginx 核心安全配置
(一)编译安装 Nginx
- 安装支持软件:Nginx 的配置和运行依赖于 pcre、zlib 等软件包,因此需要预先安装这些软件的开发包(devel),以提供相应的库和头文件,确保 Nginx 安装顺利进行。在基于 DNF 包管理器的系统(如 OpenEuler)上,执行以下命令安装相关软件:
[root@localhost ~]# dnf install -y gcc make pcre-devel zlib-devel openssl-devel perl-ExtUtils-MakeMaker git wget tar
- 创建运行用户、组和日志目录:为了提高安全性,创建专门的 Nginx 运行用户和组,并设置相应的日志目录。执行以下命令:
[root@localhost ~]# useradd -M -s /sbin/nologin nginx
[root@localhost ~]# mkdir -p /var/log/nginx
[root@localhost ~]# chown -R nginx:nginx /var/log/nginx
- 编译安装 Nginx:下载 Nginx 源码包并解压,进入解压目录进行配置和编译安装。假设下载的 Nginx 版本为 1.26.3,执行以下命令:
[root@localhost ~]# tar zxf nginx-1.26.3.tar.gz
[root@localhost ~]# cd nginx-1.26.3
[root@localhost nginx-1.26.3]#./configure \
--prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module
[root@localhost nginx-1.26.3]# make && make install
上述./configure
命令中的参数含义如下:
--prefix=/usr/local/nginx
:指定 Nginx 的安装路径。--user=nginx
和--group=nginx
:指定 Nginx 运行的用户和组。--with-http_ssl_module
:启用 HTTPS 支持。--with-http_v2_module
:启用 HTTP/2 支持。--with-http_realip_module
:用于获取客户端真实 IP 地址。--with-http_stub_status_module
:启用状态统计模块。
安装完成后,可以通过以下命令检查 Nginx 是否安装成功,并查看版本号:
[root@localhost ~]# curl -I 192.168.10.101
如果 Nginx 正常运行,会返回类似如下信息,其中包含 Nginx 的版本号:
HTTP/1.1 200 OK
Server: nginx/1.26.3
# 省略部分内容
(二)隐藏版本号
Nginx 的版本号可能会被攻击者利用,获取服务器的相关信息,增加安全风险。因此,需要隐藏 Nginx 的版本号。修改 Nginx 配置文件nginx.conf
,在http
块中添加server_tokens off;
配置项:
[root@localhost ~]# vi /usr/local/nginx/conf/nginx.conf
http {
include mime.types;
default_type application/octet-stream;
# 隐藏版本号
server_tokens off;
# 省略部分内容
}
修改完成后,检查配置文件语法是否正确,并重新加载配置:
[root@localhost ~]# nginx -t
[root@localhost ~]# nginx -s reload
再次使用curl -I
命令检查,版本号已被隐藏:
[root@localhost ~]# curl -I 192.168.10.101
HTTP/1.1 200 OK
Server: nginx
# 省略部分内容
(三)限制危险请求方法
TRACE、PUT、DELETE、CONNECT 等请求方法可能存在安全风险,如 TRACE 易引发 XST 攻击,PUT/DELETE 存在文件修改风险,CONNECT 可能导致代理滥用。通过正则表达式匹配请求方法,对非白名单方法返回 444(无响应关闭连接)。在 Nginx 配置文件nginx.conf
的server
块中添加如下配置:
[root@localhost ~]# vi /usr/local/nginx/conf/nginx.conf
server {
if ($request_method!~ ^(GET|HEAD|POST)$) {
return 444;
}
# 省略部分内容
}
修改完成后,同样检查配置文件语法并重新加载配置:
[root@localhost ~]# nginx -t
[root@localhost ~]# nginx -s reload
验证测试请求,以 PUT 请求为例:
[root@localhost ~]# curl -XPUT -I 192.168.10.101
curl: (52) Empty reply from server
查看access.log
日志,可以看到相应的记录:
192.168.10.101 - - [11/Mar/2025:18:30:46 +0800] "PUT / HTTP/1.1" 444 0 "-" "curl/8.4.0"
需要注意的是,测试 TRACE 和 CONNECT 方法时,状态码可能不是 444。原因如下:
- CONNECT 请求的目标不是代理服务器时,服务器必须返回 400 Bad Request,Nginx 核心层在请求解析阶段直接拦截,根本不进入后续的 location 处理流程。
- 现代 Nginx 默认禁用 TRACE 方法,在
ngx_http_core_module
阶段直接返回 405 Not Allowed。
(四)请求限制(CC 攻击防御)
CC 攻击(Challenge Collapsar 攻击)通过大量合法或伪造的小流量请求耗尽服务器资源,导致正常用户无法访问网站。可以使用 Nginx 的limit_req
模块限制请求速率,使用limit_conn
模块限制并发连接数。
- 使用 limit_req 模块限制请求速率:编辑 Nginx 配置文件
nginx.conf
,在http
块中定义限制区,在server
块的location
中实施速率限制:
[root@localhost ~]# vi /usr/local/nginx/conf/nginx.conf
http {
# 定义限制区(10MB内存/每秒10请求)
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# 其他全局配置...
server {
location / {
root html;
index index.html index.php;
limit_req zone=req_limit burst=20 nodelay;
}
}
}
上述配置中关键参数说明如下:
limit_req_zone
:定义共享内存区。$binary_remote_addr
:内置变量,表示客户端 IP 地址的二进制格式。zone=req_limit:10m
:创建名为req_limit
的共享内存区,大小为 10M,用于存储客户端 IP。rate=10r/s
:限制并发数,每个 IP 每秒可以发起的请求次数为 10 次。limit_req
:实施速率限制。zone=req_limit
:绑定到预定义的共享内存区。burst=20
:类似等候区,超出并发数的请求会进入等候区,等候区占满后,多余的请求会立刻返回 503。nodelay
:立即处理突发请求而不延迟,相当于立即处理等候区的请求,多余的请求会立刻返回 503。
- 压力测试验证:安装 ApacheBench(简称 ab)测试工具,它是 Apache HTTP 服务器自带的轻量级、易用的 HTTP 服务器性能测试工具。执行以下命令安装:
[root@localhost ~]# dnf install httpd-tools -y
发起测试请求,共发起 300 个请求,每次发起 30 个请求:
[root@localhost ~]# ab -n 300 -c 30 http://192.168.10.101/
其中,-n 300
表示总请求数为 300 次,即模拟客户端向服务器发送 300 次 HTTP 请求;-c30
表示并发用户数为 30,即同时有 30 个请求并行发送到服务器。
查看access.log
日志,会发现大量请求日志状态码为 503:
[root@localhost ~]# tail -300 /usr/local/nginx/logs/access.log | grep -c 503
279
(五)防盗链
防盗链用于防止未经授权的用户盗用网站(静态)资源,保护网站的带宽和资源,避免版权侵犯。盗链行为是指一个网站在没有自身页面资源的情况下,链接到其他网站的资源展示给浏览者,增加自身访问量,同时损害原网站的合法利益,加重服务器负担。
- 准备工作:本实验需要两台主机,分别为源主机(
192.168.10.101
,域名www.aaa.com
)和盗链主机(192.168.10.102
,域名www.bbb.com
)。修改 Windows 的C:\Windows\System32\drivers\etc\hosts
文件以及两台 OpenEuler 主机的hosts
文件,设置域名和 IP 映射关系:
192.168.10.101 www.aaa.com
192.168.10.102 www.bbb.com
在源主机(www.aaa.com
)的工作目录(假设为/usr/local/nginx/html
)下放置图片kgc.png
,并编辑原网站首页文件index.html
:
[root@localhost ~]# vi /usr/local/nginx/html/index.html
<html>
<body>
<h1>aaa It work!</h1>
<img src="kgc.png"/>
</body>
</html>
- 测试盗链:在盗链主机上安装
httpd
服务(用于模拟盗链网站),编辑盗链网站首页文件index.html
:
[root@localhost ~]# dnf -y install httpd
[root@localhost ~]# vi /usr/local/nginx/html/index.html
<html>
<body>
<h1>bbb It work!</h1>
<img src="http://www.aaa.com/kgc.png"/>
</body>
</html>
[root@localhost ~]# systemctl stop firewalld
[root@localhost ~]# systemctl start httpd
此时访问盗链网站(www.bbb.com
),可以看到盗链成功,显示出源网站的图片。
3. 配置 Nginx 防盗链:在源主机的 Nginx 配置文件nginx.conf
中添加防盗链配置:
[root@localhost ~]# vi /usr/local/nginx/conf/nginx.conf
location ^* \.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|webp|ico)$ {
root html;
valid_referers aaa.com *.aaa.com;
if ($invalid_referer) {
return 403;
}
}
上述配置中:
^*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|webp|ico)$
:这段正则表达式表示匹配不区分大小写,以.jpg
或.gif
或.swf
等结尾的文件。valid_referers
:设置信任的网站,可以正常使用图片。if ($invalid_referer)
:如果链接的来源域名不在valid_referers
所列出的列表中,$invalid_referer
为 1,则执行后面的操作,即返回 403 页面。
修改完成后,检查配置文件语法并重新加载配置:
[root@localhost ~]# nginx -t
[root@localhost ~]# nginx -s reload
再次访问盗链网站(www.bbb.com
),会发现盗链失败,显示 403 页面。
三、Nginx 高级防护
(一)动态黑名单
动态黑名单是 Nginx 中一种实时拦截恶意请求的安全机制,允许在不重启服务的情况下,动态更新需要封禁的 IP 地址或网段,相比静态配置的allow/deny
指令,更加灵活高效,适用于高并发、多变的攻击防护场景。
- 编辑黑名单配置文件:创建或编辑黑名单配置文件
blockips.conf
,添加需要封禁的 IP 地址或网段,以及对应的封禁动作:
[root@localhost ~]# vi /usr/local/nginx/conf/blockips.conf
192.168.1.0/24 1; # 封禁整个网段
192.168.10.102 1; # 封禁ip
IP 地址后的数字含义如下:
0
:允许访问。1
:返回 403,完全封禁。2
:返回 444,静默断开。3
:返回 503,服务不可用。
- 编辑主配置文件:在 Nginx 主配置文件
nginx.conf
中,使用geo
模块引入黑名单配置,并设置相应的封禁逻辑:
[root@localhost ~]# vi /usr/local/nginx/conf/nginx.conf
http {
geo $block_ip {
default 0; # 默认允许访问
include /usr/local/nginx/conf/blockips.conf; # 包含黑名单
}
server {
if ($block_ip) {
return 403; # 封禁动作
}
}
}
上述配置中:
geo
:Nginx 内置模块指令,专门用于处理 IP 地址相关的逻辑,基于客户端 IP 地址生成一个变量值,用于后续的访问控制判断。$block_ip
:自定义的变量名,存储计算结果(通常为 0 或 1)。default 0
:默认值,表示不在黑名单中的 IP 允许访问。if ($block_ip)
:当变量值为 1 时触发封禁逻辑。
修改完成后,检查配置文件语法并重新加载配置:
[root@localhost ~]# nginx -t
[root@localhost ~]# nginx -s reload
- 使用封禁 ip 测试访问:使用封禁的 IP(如
192.168.10.102
)访问 Nginx 服务器,会返回 403 Forbidden 页面:
[root@localhost ~]# curl 192.168.10.101
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
- 自动添加黑名单:可以编写脚本实现自动封禁访问超过一定次数的 IP。例如,以下脚本可以自动封禁访问超过 100 次的 IP:
#!/bin/bash
# 自动封禁访问超过100次的IP
awk '{print $1}' /var/log/nginx/access.log |sort |uniq -c|sort -nr | awk '{if($1>100) print $2" 1;"}' > /usr/local/nginx/conf/blockips.conf
上述脚本中:
awk '{print $1}' /var/log/nginx/access.log
:从access.log
日志文件中提取客户端 IP 地址。sort
:对提取的 IP 地址进行排序。uniq -c
:统计连续出现的次数,并在行首显示次数。sort -nr
:按数值从大到小排序。awk '{if($1>100) print $2" 1;"}'
:筛选出访问次数大于 100 的 IP 地址,并添加封禁标记(返回 403)。> /usr/local/nginx/conf/blockips.conf
:将结果输出到黑名单配置文件中。
(二)Nginx HTTPS 配置
- HTTPS 概念
- HTTPS 的设计初衷与发展:HTTPS(HyperText Transfer Protocol over Secure Socket Layer)的设计初衷是为了保证数据传输安全。国内大型互联网巨头从 2016 年开始大力推行 HTTPS,期间发生了一系列重大事件。例如,Google 搜索引擎让 HTTPS 的网站在搜索排名中更靠前;从 2017 年开始,chrome 浏览器把只采用 HTTP 的网站标记为不安全网站;苹果要求 App Store 中的所有应用都必须使用 HTTPS 加密链接;新一代的 http/2 协议的支持需要以 HTTPS 为基础。
- HTTP 不安全的原因:HTTP 由于是明文传输,主要存在三大风险:窃听风险、篡改风险、冒充风险。
- 窃听风险:中间人可以获取到通信内容,由于内容是明文,所以获取明文后有安全风险。例如在转账场景中,中间人可以截获 “转账 100” 的信息,造成用户信息泄露和资金安全隐患 。
- 篡改风险:中间人可以篡改报文内容后再发送给对方,风险极大。如将 “转账 100 到 A 账户” 篡改为 “转账 200 到其他账户” 。
- 冒充风险:用户可能误以为在和合法网站通信,但实际上是在和钓鱼网站通信。比如以为是在和某宝通信,结果却是在和钓鱼网站交互。
- 安全通信的四大原则:安全的通信需要包括机密性、完整性、身份认证和不可否认这四个原则。
- 机密性:对数据加密,解决了窃听风险。即使数据被中间人窃听,由于数据是加密的,也无法获取明文。
- 完整性:数据在传输过程中没有被篡改,接收方能够识别数据是否被修改。一旦数据有任何改动,接收报文就会被判定为不合法。
- 身份认证:确认对方的真实身份,解决了冒充风险。让用户不用担心访问的是正规网站却实际与钓鱼网站通信的问题。
- 不可否认:不可否认已发生的行为。避免出现类似小明向小红借钱却不承认的情况,保障交易或行为的可追溯性和不可抵赖性。
- HTTPS 通信原理简述:
- 对称加密的困境:HTTP 是明文传输,给报文加密可采用对称加密方式,即通信双方使用同一把密钥进行加解密。但问题在于,对称加密的密钥协商存在困难。如果通过报文直接传输密钥,密钥会被中间人截获和替换,导致后续通信依然不安全。例如,客户端向服务器请求密钥,中间人截获真密钥并返回假密钥,客户端使用假密钥加密报文,中间人就能用假密钥解密并篡改内容,再用真密钥加密篡改后的内容发送给服务器 。
- 非对称加密的引入:非对称加密即加解密双方使用不同的密钥,一把作为公钥可以公开,一把作为私钥不能公开。公钥加密的密文只有私钥可以解密,私钥签名的内容只有公钥可以验签。服务器保管好私钥,发布公钥给客户端,客户端用公钥加密对称加密的密钥(或用于生成对称加密密钥的信息)传给服务器,服务器用私钥解密获得对称加密密钥,之后双方就可以用对称加密密钥进行通信。
- 数字证书解决公钥传输信任问题:然而,服务器的公钥在传输过程中也存在被中间人调包的风险。为解决此问题,引入数字证书。服务器向证书颁发机构(CA)申请证书,在证书中附上公钥,客户端收到证书后,通过验证证书的合法性以及证书中的域名信息,来确认公钥的真实性。客户端拿到服务器端的公钥后,用公钥加密会话密钥(对称加密的密钥)发送给服务端,服务端用私钥解密获得会话密钥,然后双方就可以安全地进行数据传输。需要注意的是,HTTPS 加密采用混合模式,在握手阶段使用非对称加密,在数据传输阶段使用对称加密,因为对称加密的密钥管理简单且加密和解密效率更高。同时,中间人无法通过申请受信任的证书来替换服务器的证书,因为每个证书中的域名是唯一的,客户端会验证证书中的域名与实际访问的域名是否一致。
- Nginx 配置 HTTPS 证书
- 使用 openssl 生成证书和私钥:由于 SSL 证书需要向 CA 组织申购,在实验环境中可以采用自签名证书(自己给自己签名并颁发证书,这种证书不被信任,仅适用于测试场景)。
- 创建证书存储目录:执行以下命令创建证书存储目录:
- 使用 openssl 生成证书和私钥:由于 SSL 证书需要向 CA 组织申购,在实验环境中可以采用自签名证书(自己给自己签名并颁发证书,这种证书不被信任,仅适用于测试场景)。
[root@localhost ~]# mkdir -p /etc/nginx/ssl
- 生成自签名证书:使用 openssl 命令生成自签名证书和私钥,执行以下命令:
[root@localhost ~]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx-selfsigned.key -out /etc/nginx/ssl/nginx-selfsigned.crt -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=localhost"
- 参数解释:
-x509
:生成自签名证书(而非 CSR)。-nodes
:不加密私钥(无密码保护)。-days 365
:证书有效期 1 年。-keyout
:指定私钥文件。-out
:指定自签名文件。-newkey rsa:2048
:生成 2048 位的 RSA 私钥。-subj
:设置证书主题信息(可按需修改字段)。
- CA 签名证书与自签名证书对比:
- CA 签名证书:需要由受信任的第三方证书颁发机构(CA)签发。流程为用户生成私钥和 CSR(证书签名请求),将 CSR 提交给 CA(如 Let's Encrypt、DigiCert 等),CA 机构验证身份后,用 CA 的私钥对证书签名,生成最终证书。这种证书具有较高的信任度,适用于正式的线上环境。
- 自签名证书:证书的颁发者(Issuer)和主体(Subject)是同一个实体(即自己)。无需第三方 CA 参与,直接用工具(如 OpenSSL)生成私钥和证书。签名时使用自己的私钥,而不是 CA 的私钥。适用于测试、内部环境或无需公开信任的场景。
- Nginx 启用 HTTPS:编辑 Nginx 配置文件
nginx.conf
,添加 HTTPS 相关配置:
[root@localhost ~]# vi /usr/local/nginx/conf/nginx.conf
server {
listen 443 ssl; # 监听HTTPS端口
server_name localhost; # 域名或IP
# 指定证书和私钥路径
ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt;
ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key;
# SSL协议和加密套件配置(可选,提升安全性)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
# 其他配置(如根目录)
location / {
root /usr/local/nginx/html;
index index.html;
}
# 可选:将HTTP请求重定向到HTTPS
server {
listen 80;
server_name localhost;
return 301 https://$host$request_uri;
}
}
- 配置说明:
listen 443 ssl
:表示 Nginx 监听 443 端口,并启用 SSL 加密。server_name
:指定服务器的域名或 IP 地址。ssl_certificate
和ssl_certificate_key
:分别指定 SSL 证书和私钥的路径。ssl_protocols
:指定允许的 SSL/TLS 协议版本,这里启用了 TLSv1.2 和 TLSv1.3,TLSv1.0 和 TLSv1.1 因存在安全风险已被弃用。ssl_ciphers
:设置加密套件,选择安全强度较高的加密算法组合。ssl_prefer_server_ciphers on
:表示优先使用服务器端配置的加密套件。location /
:定义网站的根目录和默认首页。- 第二个
server
块配置用于将 HTTP 请求重定向到 HTTPS,确保所有访问都通过加密连接进行。
- 检查配置并重启 Nginx:修改完成后,检查配置文件语法是否正确,并重新加载 Nginx 配置:
[root@localhost ~]# nginx -t
[root@localhost ~]# nginx -s reload
- 通过浏览器验证:访问
https://你的服务器ip
,由于使用的是自签名证书,浏览器会提示证书不安全。在测试环境中,可以选择 “高级”-“继续前往” 或 “信任此证书”(在正式环境中,应使用受信任的 CA 签名证书)。如果配置正确,将能够正常访问网站内容。