跳转至

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_certificatessl_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 + PHP1.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.bakindex.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 评分)