Apache HTTP Server 完全指南 / SSL/TLS 配置
SSL/TLS 配置
SSL/TLS 是保护 Web 通信安全的基础技术。本章详细介绍如何在 Apache 中配置和优化 SSL/TLS。
1. SSL/TLS 基础
1.1 协议版本
| 协议 | 年份 | 安全性 | 建议 |
|---|---|---|---|
| SSLv2 | 1995 | 不安全 | 禁用 |
| SSLv3 | 1996 | 不安全(POODLE) | 禁用 |
| TLSv1.0 | 1999 | 较弱 | 禁用 |
| TLSv1.1 | 2006 | 较弱 | 禁用 |
| TLSv1.2 | 2008 | 安全 | 推荐 |
| TLSv1.3 | 2018 | 最安全 | 强烈推荐 |
1.2 证书类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
| DV(域名验证) | 验证域名所有权 | 个人网站、博客 |
| OV(组织验证) | 验证组织身份 | 企业网站 |
| EV(扩展验证) | 严格身份验证 | 金融、电商 |
| 通配符证书 | 保护所有子域名 | 多子域名网站 |
| 多域名证书(SAN) | 保护多个域名 | 多站点 |
1.3 Let’s Encrypt
Let’s Encrypt 提供免费的 DV 证书,由 ISRG(Internet Security Research Group)运营。
# 安装 Certbot
sudo apt install certbot python3-certbot-apache
# 或使用 snap
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
2. SSL 模块启用
2.1 启用 mod_ssl
# Debian/Ubuntu
sudo a2enmod ssl
sudo systemctl reload apache2
# CentOS/RHEL
# 确保 httpd.conf 中包含:
# LoadModule ssl_module modules/mod_ssl.so
# 检查模块
apachectl -M | grep ssl
2.2 生成自签名证书(测试)
# 生成自签名证书
sudo openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout /etc/ssl/private/selfsigned.key \
-out /etc/ssl/certs/selfsigned.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Test/CN=localhost"
# 生成 DH 参数(推荐)
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
2.3 Let’s Encrypt 证书申请
# 自动申请并配置
sudo certbot --apache -d example.com -d www.example.com
# 仅申请证书
sudo certbot certonly --apache -d example.com
# 使用 Webroot 方式
sudo certbot certonly --webroot -w /var/www/html -d example.com
# 使用 Standalone 方式(停止 Apache)
sudo systemctl stop apache2
sudo certbot certonly --standalone -d example.com
sudo systemctl start apache2
证书文件位置:
/etc/letsencrypt/live/example.com/
├── cert.pem # 证书
├── chain.pem # 证书链
├── fullchain.pem # 完整证书链
└── privkey.pem # 私钥
3. 基本 SSL 配置
3.1 虚拟主机配置
# /etc/apache2/sites-available/example-ssl.conf
<VirtualHost *:443>
ServerName www.example.com
ServerAlias example.com
DocumentRoot /var/www/example.com/public_html
# 基本 SSL 配置
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# 日志
ErrorLog /var/www/example.com/logs/ssl-error.log
CustomLog /var/www/example.com/logs/ssl-access.log combined
</VirtualHost>
3.2 HTTP 到 HTTPS 重定向
# 方法 1:mod_rewrite(推荐)
<VirtualHost *:80>
ServerName www.example.com
RewriteEngine On
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
# 方法 2:Redirect 指令
<VirtualHost *:80>
ServerName www.example.com
Redirect permanent / https://www.example.com/
</VirtualHost>
# 方法 3:RedirectMatch
<VirtualHost *:80>
ServerName www.example.com
RedirectMatch permanent ^/(.*)$ https://www.example.com/$1
</VirtualHost>
3.3 中间证书链配置
# 方法 1:使用 SSLCertificateChainFile(旧版)
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
SSLCertificateChainFile /etc/ssl/certs/chain.pem
# 方法 2:使用 fullchain.pem(推荐)
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
4. 安全强化配置
4.1 协议和密码套件
# /etc/apache2/conf-available/ssl-params.conf
# 仅允许安全协议
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# 推荐密码套件(Mozilla Intermediate)
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
# DH 参数
SSLOpenSSLConfCmd DHParameters /etc/ssl/certs/dhparam.pem
# SSL 会话配置
SSLSessionCache shm:/var/run/ssl_scache(512000)
SSLSessionCacheTimeout 300
# SSL 压缩(禁用,防止 CRIME 攻击)
SSLCompression off
# OCSP Stapling
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/ocsp(128000)
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
4.2 安全头配置
# 启用 mod_headers
# sudo a2enmod headers
# HSTS(HTTP 严格传输安全)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# 其他安全头
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# CSP(内容安全策略)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
# 预加载 HSTS(可选,提交到 hstspreload.org)
# Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
4.3 完整安全配置模板
# /etc/apache2/conf-available/ssl-security.conf
<IfModule mod_ssl.c>
# 协议
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# 密码套件
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
# 会话
SSLSessionTickets off
SSLSessionCache shm:/var/run/ssl_scache(512000)
SSLSessionCacheTimeout 300
# 压缩
SSLCompression off
# OCSP
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/ocsp(128000)
</IfModule>
<IfModule mod_headers.c>
# HSTS
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# 安全头
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
</IfModule>
5. 证书管理
5.1 证书自动续期
# 测试续期
sudo certbot renew --dry-run
# 设置自动续期(cron)
sudo crontab -e
# 添加:
# 0 3 * * * certbot renew --quiet --post-hook "systemctl reload apache2"
# 或使用 systemd timer
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
# 检查 timer 状态
sudo systemctl status certbot.timer
5.2 证书续期钩子
# /etc/letsencrypt/renewal-hooks/deploy/reload-apache.sh
#!/bin/bash
systemctl reload apache2
# /etc/letsencrypt/renewal-hooks/deploy/notify.sh
#!/bin/bash
echo "SSL certificate renewed for $(hostname)" | \
mail -s "SSL Renewal" [email protected]
# 设置执行权限
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/*.sh
5.3 证书验证
# 检查证书信息
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text -noout
# 检查证书有效期
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -dates -noout
# 检查远程服务器证书
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -text -noout
# 检查证书链
openssl s_client -connect example.com:443 -servername example.com -showcerts
# 测试 SSL 配置
# 使用 ssllabs.com
# 使用 testssl.sh
git clone https://github.com/drwetter/testssl.sh.git
cd testssl.sh
./testssl.sh https://example.com
5.4 证书吊销
# Let's Encrypt 证书吊销
sudo certbot revoke --cert-name example.com
# 使用 OpenSSL 吊销(自签名/私有 CA)
openssl ca -revoke /etc/ssl/certs/example.com.crt -config /etc/ssl/openssl.cnf
6. 多站点 SSL 配置
6.1 SNI(Server Name Indication)
SNI 允许在一个 IP 地址上托管多个 SSL 站点。
# 站点 A
<VirtualHost *:443>
ServerName site-a.example.com
DocumentRoot /var/www/site-a
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/site-a.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/site-a.example.com/privkey.pem
</VirtualHost>
# 站点 B
<VirtualHost *:443>
ServerName site-b.example.com
DocumentRoot /var/www/site-b
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/site-b.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/site-b.example.com/privkey.pem
</VirtualHost>
# 通配符证书
<VirtualHost *:443>
ServerName example.com
ServerAlias *.example.com
DocumentRoot /var/www/example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>
6.2 通配符证书申请
# 使用 DNS 验证
sudo certbot certonly --manual --preferred-challenges dns \
-d example.com -d "*.example.com"
# 使用 Cloudflare DNS 插件
sudo apt install python3-certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com -d "*.example.com"
# Cloudflare 凭证文件
# /etc/letsencrypt/cloudflare.ini
# dns_cloudflare_email = [email protected]
# dns_cloudflare_api_key = your-api-key
7. 性能优化
7.1 SSL 会话优化
# 会话缓存
SSLSessionCache shm:/var/run/ssl_scache(512000)
SSLSessionCacheTimeout 300
# 禁用会话票证(更安全)
SSLSessionTickets off
# 如果启用会话票证,定期轮换密钥
# SSLSessionTicketKeyFile /etc/apache2/ssl/ticket.key
7.2 OCSP Stapling
# 启用 OCSP Stapling
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/ocsp(128000)
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
7.3 HTTP/2 优化
# 启用 HTTP/2
# sudo a2enmod http2
Protocols h2 http/1.1
# 或在虚拟主机中配置
<VirtualHost *:443>
Protocols h2 http/1.1
SSLEngine on
# ...
</VirtualHost>
8. 业务场景
8.1 电商网站 SSL 配置
<VirtualHost *:443>
ServerName shop.example.com
DocumentRoot /var/www/shop
SSLEngine on
SSLCertificateFile /etc/ssl/certs/shop.example.com.crt
SSLCertificateKeyFile /etc/ssl/private/shop.example.com.key
SSLCertificateChainFile /etc/ssl/certs/chain.pem
# 严格安全协议
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on
# HSTS 预加载
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# CSP
Header always set Content-Security-Policy "upgrade-insecure-requests"
</VirtualHost>
8.2 API 服务器 SSL
<VirtualHost *:443>
ServerName api.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/api.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/api.example.com/privkey.pem
# 客户端证书验证(可选)
SSLVerifyClient optional
SSLVerifyDepth 2
SSLCACertificateFile /etc/ssl/certs/client-ca.crt
# 传递客户端证书信息
RequestHeader set X-Client-Cert "%{SSL_CLIENT_S_DN}s"
</VirtualHost>
8.3 内部服务 SSL
# 使用自签名证书的内部服务
<VirtualHost *:443>
ServerName internal.example.local
SSLEngine on
SSLCertificateFile /etc/ssl/internal/server.crt
SSLCertificateKeyFile /etc/ssl/internal/server.key
# 仅允许内部访问
<Directory "/var/www/internal">
Require ip 10.0.0.0/8
Require ip 172.16.0.0/12
Require ip 192.168.0.0/16
</Directory>
</VirtualHost>
9. 故障排除
9.1 常见错误
# 错误:SSL: error:0906D06C:PEM routines:PEM_read_bio:no start line
# 原因:证书文件格式错误
# 解决:检查证书文件内容
openssl x509 -in cert.pem -text -noout
# 错误:SSL: error:14094418:SSL routines:ssl3_read_bytes:tlv1 alert unknown ca
# 原因:证书链不完整
# 解决:使用 fullchain.pem
# 错误:AH01903: Failed to configure CA certificate
# 原因:CA 证书路径错误
# 解决:检查 SSLCACertificateFile 路径
# 错误:SSLConnectionError
# 原因:端口未监听或防火墙阻止
# 解决:检查端口和防火墙配置
9.2 调试命令
# 检查 SSL 配置
apachectl -t -D DUMP_MODULES | grep ssl
# 测试 SSL 连接
openssl s_client -connect localhost:443 -servername example.com
# 查看证书信息
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -text -noout
# 检查 OCSP
openssl s_client -connect example.com:443 -status
# 检查协议支持
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
9.3 日志分析
# SSL 错误日志
tail -f /var/log/apache2/error.log | grep -i ssl
# 检查 SSL 握手
grep -i "ssl" /var/log/apache2/error.log
# 分析 SSL 协议使用
awk '/SSLProtocol/ {print $NF}' /var/log/apache2/access.log | sort | uniq -c
10. 注意事项
- 证书过期:设置自动续期和监控告警
- 协议安全:禁用不安全的 SSL/TLS 版本
- 密码套件:使用推荐的密码套件配置
- HSTS:谨慎启用预加载,一旦启用难以撤回
- 备份证书:定期备份证书和私钥文件
- 合规要求:根据行业要求(如 PCI DSS)配置 SSL
11. 扩展阅读
12. 总结
SSL/TLS 配置是现代 Web 服务器的必备功能:
- 证书管理:使用 Let’s Encrypt 免费获取证书
- 安全配置:禁用不安全协议,使用强密码套件
- 性能优化:OCSP Stapling、会话缓存
- 自动化:自动续期,减少人工维护
正确配置 SSL/TLS 可以保护用户数据安全,提升网站信任度。