Web 服务器
Web 服务器¶
本章导读:本章对比主流 Web 服务器(Nginx、Apache、Tomcat、IIS 等)的架构特点,详解 Nginx 配置语法、Location 匹配规则、反向代理与负载均衡机制,以及服务器层面的安全配置与漏洞分析。
Web 服务器的定义与作用¶
Web 服务器(也称 HTTP 服务器、HTTP 容器)是运行在服务器端的软件,负责处理 HTTP 请求并返回响应。其核心功能:
-
监听端口(默认 80/443),接收并解析 HTTP 请求
-
返回静态资源(HTML、CSS、JS、图片、字体、视频等),直接读取文件系统响应
-
将动态请求转发给后端应用处理(如 PHP-FPM、Java Servlet、Python WSGI/ASGI、Node.js),通过 CGI、FastCGI、AJP、HTTP 代理等协议通信
-
提供反向代理、负载均衡、SSL/TLS 终止、缓存、压缩、重写、访问控制等高级功能
-
记录访问日志(Access Log)和错误日志(Error Log),用于安全审计、性能分析和故障排查
主流 Web 服务器详解¶
Nginx¶
-
由俄罗斯开发者 Igor Sysoev 创建,事件驱动(Event-Driven)架构,高并发、低资源占用
-
采用 Master-Worker 多进程模型:Master 进程管理配置加载和 Worker 进程,Worker 进程处理客户端请求(每个 Worker 单线程,异步非阻塞)
-
擅长反向代理、静态资源服务、负载均衡,也可通过模块支持 Lua(OpenResty)、njs(JavaScript)扩展
-
市场份额最高,广泛用于 CDN、API 网关、微服务入口
Apache HTTP Server(httpd)¶
-
由 Apache 软件基金会维护,历史悠久(1995 年),模块丰富(MPM 多处理模块:prefork、worker、event)
-
支持
.htaccess分布式配置,每个目录可独立配置,适合共享主机环境 -
动态内容处理能力强(mod_php、mod_python、mod_wsgi),但高并发下性能不如 Nginx
-
配置灵活,通过
mod_rewrite实现强大的 URL 重写
Tomcat¶
-
Apache 基金会开源的 Java Servlet 容器,实现了 Java EE 的 Servlet 和 JSP 规范
-
连接器(Connector)支持 HTTP/1.1(BIO/NIO/APR)和 AJP(Apache JServ Protocol)
-
容器(Container)层级:Engine → Host → Context → Wrapper(Servlet)
-
通过 Valve(阀门)实现请求处理链的拦截(如访问日志、远程地址过滤、单点登录)
-
通常与 Nginx/Apache 配合,前端处理静态资源,Tomcat 处理动态 Java 请求
IIS(Internet Information Services)¶
-
微软 Windows Server 集成,支持 ASP.NET、ASP、PHP(通过 FastCGI)
-
模块化架构(Integrated Pipeline),请求处理管道可插入自定义模块
-
管理通过 IIS Manager GUI 或 PowerShell 命令
-
与 Windows 身份验证(Active Directory、NTLM、Kerberos)深度集成
WebLogic / WebSphere / JBoss(WildFly)¶
-
企业级 Java EE 应用服务器,支持 EJB、JMS、JTA、JPA 等完整 Java EE 规范
-
提供集群、高可用、事务管理、消息队列等企业级功能
-
配置复杂,历史上漏洞较多(如 WebLogic 的 T3 协议反序列化漏洞 CVE-2018-2628、CVE-2019-2725)
Caddy¶
-
使用 Go 语言编写,配置极简(Caddyfile),默认自动 HTTPS(通过 Let's Encrypt 自动申请证书)
-
内置 HTTP/2、HTTP/3 支持、动态配置(API 热重载)
-
适合个人项目、云原生环境、快速部署
Nginx 配置详解¶
Nginx 配置文件通常位于 /etc/nginx/nginx.conf,采用层级块结构:
# 全局块:配置影响 Nginx 全局的参数
user nginx; # 运行用户
worker_processes auto; # Worker 进程数,通常等于 CPU 核心数
error_log /var/log/nginx/error.log warn; # 错误日志路径和级别
pid /var/run/nginx.pid; # PID 文件路径
events { # Events 块:配置连接处理
worker_connections 1024; # 每个 Worker 最大连接数
use epoll; # Linux 下使用 epoll 事件驱动(高效)
multi_accept on; # 尽可能多地接受新连接
}
http { # HTTP 块:配置 HTTP 服务器
include /etc/nginx/mime.types; # MIME 类型映射
default_type application/octet-stream;
# 日志格式定义
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main; # 访问日志
# 性能优化
sendfile on; # 零拷贝传输文件,减少用户态/内核态切换
tcp_nopush on; # 防止 TCP 包碎片化
tcp_nodelay on; # 禁用 Nagle 算法,减少延迟
keepalive_timeout 65; # 长连接保持时间
# Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 服务器块(虚拟主机)
server {
listen 80; # 监听端口
server_name example.com www.example.com; # 域名匹配
# 根目录和索引文件
root /var/www/html;
index index.html index.htm;
# Location 块:匹配 URL 路径并处理
location / {
try_files $uri $uri/ /index.html; # 尝试文件,否则返回 index.html(SPA 支持)
}
# 精确匹配
location = /api/health {
access_log off; # 健康检查不记录日志
return 200 "OK";
}
# 正则匹配(区分大小写)
location ~ \.(gif|jpg|png)$ {
expires 30d; # 静态图片缓存 30 天
}
# 正则匹配(不区分大小写)
location ~* \.(css|js)$ {
expires 1y; # CSS/JS 缓存 1 年
add_header Cache-Control "public, immutable";
}
# 前缀匹配(优先于正则)
location ^~ /static/ {
root /var/www/static;
}
# 反向代理到后端 API
location /api/ {
proxy_pass http://backend_server; # 转发到后端
proxy_set_header Host $host; # 传递原始 Host
proxy_set_header X-Real-IP $remote_addr; # 传递真实 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 拒绝访问敏感文件
location ~ /\. {
deny all; # 禁止访问 . 开头的隐藏文件(如 .git、.env)
}
}
# HTTPS 服务器
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.key;
ssl_protocols TLSv1.2 TLSv1.3; # 仅允许安全协议
ssl_ciphers HIGH:!aNULL:!MD5; # 加密套件白名单
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
}
Location 匹配规则详解¶
| 修饰符 | 匹配类型 | 优先级 | 示例 |
|---|---|---|---|
= |
精确匹配 | 最高(1) | location = /exact 只匹配 /exact |
^~ |
前缀匹配(优先于正则) | 高(2) | location ^~ /static/ 匹配 /static/... |
~ |
正则匹配(区分大小写) | 中(3) | location ~ \.php$ 匹配 .php 结尾 |
~* |
正则匹配(不区分大小写) | 中(3) | location ~* \.(jpg|jpeg)$ |
| 无修饰符 | 前缀匹配 | 低(4) | location /api/ 匹配 /api/... |
匹配顺序:精确匹配 → 前缀匹配(^~)→ 正则匹配(按配置文件顺序,第一个匹配生效)→ 普通前缀匹配(最长匹配)
反向代理详解¶
-
proxy_pass将请求转发到后端服务器。若 URL 带/结尾(如proxy_pass http://backend/),则替换 location 匹配部分;不带/则追加完整路径 -
常用代理头:
-
X-Real-IP:客户端真实 IP -
X-Forwarded-For:代理链路上的所有 IP -
X-Forwarded-Proto:原始协议(http/https),用于后端识别是否 HTTPS -
X-Forwarded-Host:原始 Host -
攻击者可伪造这些头,后端若直接使用这些头进行访问控制、日志记录或业务逻辑,可能导致 IP 伪造、日志污染、权限绕过
负载均衡详解¶
upstream backend_servers {
# 轮询(默认):依次分配
server 192.168.1.10:8080;
server 192.168.1.11:8080;
# 权重轮询:weight 越大分配越多
server 192.168.1.10:8080 weight=5;
server 192.168.1.11:8080 weight=1;
# 最少连接:将请求分配给当前连接数最少的服务器
least_conn;
# IP 哈希:根据客户端 IP 的哈希值分配,保证同一 IP 总是分配到同一服务器(适合会话保持)
ip_hash;
# 第三方模块:fair(按响应时间分配)、url_hash(按 URL 哈希分配,适合缓存服务器)
# 健康检查
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s; # 3 次失败则 30 秒内视为不可用
server 192.168.1.11:8080 backup; # 备用服务器,仅当其他服务器不可用时使用
}
| 负载均衡算法 | 原理 | 适用场景 |
|---|---|---|
| 轮询(Round Robin) | 依次循环分配 | 服务器性能相近 |
| 权重轮询(Weighted Round Robin) | 按权重比例分配 | 服务器性能不均 |
| 最少连接(Least Connections) | 分配给当前连接数最少的服务器 | 长连接场景(WebSocket、游戏) |
| IP 哈希(IP Hash) | 按客户端 IP 哈希值分配 | 需会话保持,无共享 Session 时 |
| 最短响应时间(Fair) | 分配给响应最快的服务器 | 动态负载感知 |
| URL 哈希(URL Hash) | 按请求 URL 哈希值分配 | 缓存服务器集群 |
SSL/TLS 配置详解¶
-
ssl_certificate和ssl_certificate_key:指定证书和私钥文件路径(PEM 格式) -
证书链:通常证书文件包含服务器证书 + 中间证书(中间证书缺失会导致部分客户端验证失败)
-
ssl_protocols:声明支持的 TLS 版本。建议仅启用 TLSv1.2 和 TLSv1.3,禁用 SSLv3、TLSv1.0、TLSv1.1(存在已知漏洞) -
ssl_ciphers:加密套件优先级。建议使用ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:!aNULL:!MD5:!DSS,禁用弱算法(DES、3DES、RC4、MD5、RSA 密钥交换) -
HSTS:
add_header Strict-Transport-Security,强制浏览器在一段时间内仅通过 HTTPS 访问 -
OCSP Stapling:服务器定期向证书颁发机构查询证书吊销状态,并随 TLS 握手返回给客户端,加速验证过程
-
证书获取:Let's Encrypt 提供免费证书,通过 ACME 协议(如 Certbot)自动申请和续期
安全测试关联点¶
服务器版本探测¶
- 服务器版本探测:通过
Server响应头(如Server: nginx/1.18.0)、错误页面、默认页面、特定行为(如 Nginx 的 404 页面样式)判断服务器类型和版本,匹配 CVE 漏洞数据库(如 Nginx 1.18.0 是否存在已知漏洞)
目录遍历¶
- 目录遍历(Directory Traversal)
配置不当导致可通过 ../ 访问服务器任意文件。如 location /files { alias /var/www/files/; } 若请求 /files../secret.txt 可能访问 /var/www/secret.txt(Nginx alias 配置漏洞)。
防御:使用 root 替代 alias、路径规范化、正则限制。
解析漏洞¶
-
解析漏洞:
-
Nginx + PHP:
1.jpg%00.php若 PHP 配置cgi.fix_pathinfo=1,Nginx 传递1.jpg给 PHP-FPM,PHP 检测到文件不存在后尝试解析路径中的.php,导致图片被当作 PHP 执行(%00 截断在 PHP 5.3.4 以下有效) -
Apache 多后缀解析:
1.php.jpg若配置AddHandler php5-script .php,Apache 从右向左匹配,.php匹配成功,当作 PHP 执行(取决于配置,mod_php 与 PHP-FPM 行为不同) -
IIS 解析漏洞:
1.asp;1.jpg被 IIS 6.0 当作 ASP 解析(分号截断)
配置泄露¶
-
配置泄露:
-
.git目录:未禁止访问时,可通过.git/config、.git/HEAD下载整个代码库(工具:GitHacker、git-dumper) -
.svn目录:类似.git,可通过.svn/entries获取代码历史 -
.env文件:包含数据库密码、API 密钥等敏感配置 -
备份文件:
index.php.bak、index.php~、index.php.swp(Vim 临时文件) -
目录列表:未配置
autoindex off时,访问目录会列出所有文件
日志分析¶
-
日志分析:安全测试需分析访问日志,发现攻击痕迹与异常行为。关注:
-
大量 404 请求:目录扫描、暴力破解
-
大量 401/403:认证绕过尝试、权限探测
-
特定 User-Agent:扫描器(如
"Mozilla/5.0 (compatible; Nmap Scripting Engine)")、爬虫 -
异常 Referer:钓鱼来源、跨站请求
-
响应体大小异常:可能的数据泄露、SQL 注入导致大量数据返回
中间件漏洞¶
-
中间件漏洞:
-
Nginx:CVE-2013-4547(空字节截断)、CVE-2017-7529(整数溢出导致信息泄露)
-
Apache:CVE-2021-41773(路径遍历导致 RCE)、CVE-2021-42013(路径遍历)
-
Tomcat:CVE-2020-1938(Ghostcat,AJP 文件读取和包含)、CVE-2019-0232(Windows 下命令执行)
-
WebLogic:CVE-2018-2628(T3 反序列化)、CVE-2019-2725(wls9-async 反序列化)、CVE-2020-14882(未授权访问后台)
HTTP 请求走私¶
- HTTP 请求走私(HTTP Request Smuggling)
利用前端代理(如 Nginx)和后端服务器(如 Node.js)对请求边界解析不一致(Content-Length vs Transfer-Encoding: chunked),将两个请求走私为一个。
防御:使用 HTTP/2(端到端避免解析差异)、配置一致的处理方式、禁用 Transfer-Encoding 重写。
Nginx 配置安全审计¶
-
Nginx 配置安全审计:
-
检查
server_tokens off;是否隐藏版本号 -
检查
autoindex是否关闭 -
检查
.git、.env、备份文件等是否被禁止访问 -
检查
proxy_pass后的后端是否可访问(直连绕过 WAF) -
检查
add_header安全头是否配置完整(CSP、HSTS、X-Frame-Options 等) -
检查 SSL 配置是否使用弱算法(SSL Labs 评分)