Appearance
HTTPS 与 TLS
HTTPS (HTTP Secure) 和 TLS (Transport Layer Security) 是保护网络通信安全的关键技术,它们通过加密传输数据来防止窃听、篡改和冒充攻击。
HTTPS 与 TLS 基础概念
什么是 HTTPS
HTTPS 是 HTTP 协议的安全版本,它通过 TLS/SSL 协议对 HTTP 通信进行加密,确保数据在客户端和服务器之间的安全传输。
什么是 TLS
TLS (传输层安全) 是 SSL (安全套接字层) 的后继协议,它提供了通信安全的三个核心要素:
- 加密 - 保护数据隐私
- 认证 - 验证通信方身份
- 完整性 - 确保数据未被篡改
TLS 协议工作原理
TLS 握手过程
mermaid
sequenceDiagram
participant Client
participant Server
Client->>Server: Client Hello (supported versions, cipher suites, random data)
Server->>Client: Server Hello (selected version, cipher suite, random data)
Server->>Client: Certificate (server's public key)
Server->>Client: Server Hello Done
Client->>Server: Client Key Exchange
Client->>Server: Change Cipher Spec
Client->>Server: Encrypted Handshake Message
Server->>Client: Change Cipher Spec
Server->>Client: Encrypted Handshake Message
TLS 1.3 与 TLS 1.2 的区别
javascript
// TLS 1.2 握手通常需要 2 个往返
// TLS 1.3 握手只需要 1 个往返,提高了性能
const tlsConfig = {
// TLS 1.3 配置示例
minVersion: 'TLSv1.3',
maxVersion: 'TLSv1.3',
ciphers: [
'TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_AES_128_GCM_SHA256'
].join(':')
};
证书管理
证书类型
javascript
// 证书配置示例
const certificateTypes = {
// DV (Domain Validated) - 域名验证
dv: {
validation: 'Domain ownership',
issuance: 'Fast (minutes)',
securityLevel: 'Basic'
},
// OV (Organization Validated) - 组织验证
ov: {
validation: 'Domain + Organization',
issuance: 'Days',
securityLevel: 'Medium'
},
// EV (Extended Validation) - 扩展验证
ev: {
validation: 'Domain + Organization + Legal Entity',
issuance: 'Weeks',
securityLevel: 'High'
}
};
自签名证书 vs CA 签名证书
javascript
// 生成自签名证书(仅用于开发)
const forge = require('node-forge');
function generateSelfSignedCert() {
const keys = forge.pki.generateKeyPair(2048);
const cert = forge.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = '01';
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
const attrs = [{
name: 'commonName',
value: 'localhost'
}, {
name: 'countryName',
value: 'US'
}, {
shortName: 'ST',
value: 'Virginia'
}, {
name: 'localityName',
value: 'Blacksburg'
}, {
name: 'organizationName',
value: 'Test Org'
}];
cert.setSubject(attrs);
cert.setIssuer(attrs);
cert.sign(keys.privateKey);
const pem = {
certificate: forge.pki.certificateToPem(cert),
privateKey: forge.pki.privateKeyToPem(keys.privateKey)
};
return pem;
}
Node.js 中的 HTTPS 配置
基础 HTTPS 服务器
javascript
const https = require('https');
const fs = require('fs');
// 基础 HTTPS 服务器配置
const options = {
key: fs.readFileSync('path/to/private-key.pem'),
cert: fs.readFileSync('path/to/certificate.pem'),
// 可选:中间证书
ca: fs.readFileSync('path/to/ca-cert.pem')
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Hello over HTTPS!' }));
});
server.listen(443, () => {
console.log('HTTPS Server running on port 443');
});
Express.js HTTPS 配置
javascript
const express = require('express');
const https = require('https');
const fs = require('fs');
const app = express();
// HTTPS 配置
const httpsOptions = {
key: fs.readFileSync(process.env.SSL_KEY_PATH),
cert: fs.readFileSync(process.env.SSL_CERT_PATH),
// 安全配置
honorCipherOrder: true,
secureProtocol: 'TLSv1_2_method', // 限制最低 TLS 版本
ciphers: [
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES256-SHA384',
'ECDHE-RSA-AES128-SHA256',
'ECDHE-RSA-AES256-SHA',
'ECDHE-RSA-AES128-SHA',
'DHE-RSA-AES256-GCM-SHA384',
'DHE-RSA-AES128-GCM-SHA256',
'AES256-GCM-SHA384',
'AES128-GCM-SHA256',
'AES256-SHA256',
'AES128-SHA256',
'AES256-SHA',
'AES128-SHA'
].join(':')
};
const server = https.createServer(httpsOptions, app);
app.get('/', (req, res) => {
res.json({ message: 'Secure app running over HTTPS' });
});
server.listen(443, () => {
console.log('Secure server listening on port 443');
});
安全配置最佳实践
HSTS (HTTP Strict Transport Security)
javascript
// HSTS 中间件
function hstsMiddleware(req, res, next) {
// 强制浏览器只使用 HTTPS
res.setHeader('Strict-Transport-Security', [
'max-age=31536000', // 一年
'includeSubDomains', // 应用于所有子域名
'preload' // 允许加入浏览器预加载列表
].join('; '));
next();
}
// 在 Express 中使用
app.use(hstsMiddleware);
安全的 TLS 配置
javascript
// 安全的 TLS 配置
const secureTlsOptions = {
// 禁用不安全的协议版本
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3',
// 安全的密码套件
ciphers: [
'ECDHE+AESGCM',
'ECDHE+CHACHA20',
'DHE+AESGCM',
'DHE+CHACHA20',
'!aNULL',
'!MD5',
'!DSS'
].join(':'),
// 启用前向保密
honorCipherOrder: true,
// 会话管理
secureOptions: require('constants').SSL_OP_NO_SSLv2 |
require('constants').SSL_OP_NO_SSLv3 |
require('constants').SSL_OP_NO_TLSv1 |
require('constants').SSL_OP_NO_TLSv1_1,
// 证书配置
requestCert: false, // 根据需要启用客户端证书验证
rejectUnauthorized: true
};
证书透明度
javascript
// 证书透明度相关配置
const ctConfig = {
// 证书透明度日志监控
monitor: {
enable: true,
logs: [
'https://ct.googleapis.com/logs/argon2022/',
'https://ct.cloudflare.com/logs/nimbus2021/'
]
},
// 证书透明度策略
policy: {
requireScts: true, // 要求 SCT (Signed Certificate Timestamps)
validateScts: true // 验证 SCT 有效性
}
};
自动证书管理
Let's Encrypt 集成
javascript
const acme = require('acme-client');
const forge = require('node-forge');
// Let's Encrypt 客户端配置
class LetsEncryptManager {
constructor(domain, contactEmail) {
this.domain = domain;
this.contactEmail = contactEmail;
this.client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.production,
accountKey: await acme.crypto.createPrivateKey()
});
}
async provisionCertificate() {
// 创建订单
const order = await this.client.createOrder({
identifiers: [
{ type: 'dns', value: this.domain }
]
});
// 完成质询
const challanges = await this.client.getAuthorizations(order);
for (const challenge of challanges) {
if (challenge.type === 'http-01') {
// 放置质询文件
await this.placeChallengeFile(challenge);
// 通知 ACME 服务器验证
await this.client.completeChallenge(challenge);
// 等待验证完成
await this.client.waitForValidStatus(challenge);
}
}
// 生成私钥并创建 CSR
const [key, csr] = await acme.crypto.createCsr({
commonName: this.domain,
altNames: [this.domain]
});
// 最终化订单并下载证书
const cert = await this.client.getCertificate(order, csr);
return { key, cert };
}
async placeChallengeFile(challenge) {
// 将质询文件放置在 .well-known/acme-challenge/ 目录
const { token, keyAuthorization } = challenge;
await fs.promises.writeFile(
`.well-known/acme-challenge/${token}`,
keyAuthorization
);
}
}
证书监控和续订
证书到期监控
javascript
const https = require('https');
const tls = require('tls');
// 检查证书到期时间
function checkCertificateExpiry(hostname, port = 443) {
return new Promise((resolve, reject) => {
const socket = tls.connect(port, hostname, {
servername: hostname
});
socket.on('secureConnect', () => {
const cert = socket.getPeerCertificate();
const validFrom = new Date(cert.valid_from);
const validUntil = new Date(cert.valid_until);
const now = new Date();
const daysUntilExpiry = Math.ceil(
(validUntil - now) / (1000 * 60 * 60 * 24)
);
resolve({
validFrom,
validUntil,
daysUntilExpiry,
isExpired: now > validUntil
});
socket.end();
});
socket.on('error', reject);
});
}
// 定期检查证书
async function scheduleCertificateCheck() {
const domains = ['example.com', 'api.example.com'];
const warningThreshold = 30; // 30天警告
for (const domain of domains) {
try {
const certInfo = await checkCertificateExpiry(domain);
if (certInfo.daysUntilExpiry <= warningThreshold) {
console.warn(`Certificate for ${domain} expires in ${certInfo.daysUntilExpiry} days`);
// 发送通知或自动续订
await notifyCertificateExpiry(domain, certInfo);
}
} catch (error) {
console.error(`Failed to check certificate for ${domain}:`, error);
}
}
}
async function notifyCertificateExpiry(domain, certInfo) {
// 实现通知逻辑(邮件、Slack、等等)
console.log(`Certificate for ${domain} expires on ${certInfo.validUntil}`);
}
中间件和框架集成
Express.js 安全中间件
javascript
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
// HTTPS 安全中间件配置
const securityMiddlewares = {
// 基础安全头
baseSecurity: helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"]
}
},
hsts: {
maxAge: 31536000, // 一年
includeSubDomains: true,
preload: true
},
frameguard: {
action: 'DENY'
},
dnsPrefetchControl: {
allow: false
},
referrerPolicy: {
policy: 'same-origin'
}
}),
// 速率限制
rateLimit: rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 限制每个IP 15分钟内最多100个请求
message: 'Too many requests, please try again later.'
}),
// 强制 HTTPS
forceHttps: (req, res, next) => {
if (process.env.NODE_ENV === 'production') {
if (!req.secure && req.get('x-forwarded-proto') !== 'https') {
return res.redirect('https://' + req.get('host') + req.url);
}
}
next();
}
};
// 应用安全中间件
app.use(securityMiddlewares.baseSecurity);
app.use(securityMiddlewares.rateLimit);
app.use(securityMiddlewares.forceHttps);
双重监听 HTTP 和 HTTPS
javascript
const express = require('express');
const http = require('http');
const https = require('https');
const fs = require('fs');
const app = express();
// HTTP 服务器 - 重定向到 HTTPS
const httpServer = http.createServer((req, res) => {
const httpsPort = process.env.HTTPS_PORT || 443;
const redirectUrl = `https://${req.headers.host.replace(/:\d+/, '')}:${httpsPort}${req.url}`;
res.writeHead(301, { Location: redirectUrl });
res.end();
});
// HTTPS 服务器
const httpsOptions = {
key: fs.readFileSync(process.env.SSL_KEY_PATH),
cert: fs.readFileSync(process.env.SSL_CERT_PATH)
};
const httpsServer = https.createServer(httpsOptions, app);
// 路由
app.get('/', (req, res) => {
res.json({ message: 'Secure API endpoint' });
});
// 同时监听 HTTP 和 HTTPS
httpServer.listen(80, () => {
console.log('HTTP server running on port 80, redirecting to HTTPS');
});
httpsServer.listen(443, () => {
console.log('HTTPS server running on port 443');
});
性能优化
TLS 会话复用
javascript
// TLS 会话复用配置
const tlsSessionCache = new Map();
const secureHttpsOptions = {
key: fs.readFileSync(process.env.SSL_KEY_PATH),
cert: fs.readFileSync(process.env.SSL_CERT_PATH),
// 会话复用
secureOptions: require('constants').SSL_OP_NO_SSLv2 |
require('constants').SSL_OP_NO_SSLv3,
// 会话票证
ticketKeys: Buffer.from(process.env.TLS_TICKET_KEYS, 'hex'),
// 会话 ID 回调
sessionIdContext: 'my-secure-app',
maxSessionMemory: 128, // MB
// 会话处理
createConnection: (options) => {
const sessionKey = options.servername || 'default';
const session = tlsSessionCache.get(sessionKey);
if (session) {
options.session = session;
}
const socket = new tls.TLSSocket(socket, options);
socket.on('session', (session) => {
tlsSessionCache.set(sessionKey, session);
});
return socket;
}
};
监控和日志
TLS 连接监控
javascript
// TLS 连接监控中间件
function tlsMonitoringMiddleware(req, res, next) {
const startTime = Date.now();
// 记录 TLS 信息
if (req.connection.encrypted) {
const tlsInfo = {
protocol: req.connection.getProtocol(),
cipher: req.connection.getCipher(),
peerCertificate: req.connection.getPeerCertificate(),
authorized: req.connection.authorized
};
// 记录连接信息
console.log('TLS Connection:', {
timestamp: new Date().toISOString(),
remoteAddress: req.connection.remoteAddress,
cipher: tlsInfo.cipher.name,
version: tlsInfo.protocol,
duration: null
});
// 记录响应完成时间
res.on('finish', () => {
const duration = Date.now() - startTime;
console.log(`Request completed in ${duration}ms`);
});
}
next();
}
app.use(tlsMonitoringMiddleware);
常见错误和解决方案
证书错误处理
javascript
// 处理 TLS 错误
function handleTlsErrors(server) {
server.on('tlsClientError', (err, tlsSocket) => {
console.error('TLS Client Error:', {
error: err.message,
code: err.code,
remoteAddress: tlsSocket.remoteAddress,
remotePort: tlsSocket.remotePort
});
// 记录失败的握手尝试
if (err.code === 'ERR_SSL_WRONG_VERSION_NUMBER') {
console.warn('Client attempting wrong TLS version');
} else if (err.code === 'ERR_SSL_SSL') {
console.warn('General SSL error');
}
});
}
// 应用错误处理
handleTlsErrors(httpsServer);
证书链验证
javascript
// 验证证书链
function validateCertificateChain(cert) {
const errors = [];
// 检查有效期
const now = new Date();
if (now < new Date(cert.valid_from)) {
errors.push('Certificate not yet valid');
}
if (now > new Date(cert.valid_until)) {
errors.push('Certificate expired');
}
// 检查主题
if (!cert.subject.CN) {
errors.push('Missing Common Name');
}
// 检查 SAN
if (cert.subjectaltname) {
const sans = cert.subjectaltname.split(', ');
// 验证 SAN 包含正确的域名
}
return {
valid: errors.length === 0,
errors
};
}
最佳实践总结
配置检查清单
javascript
const tlsBestPractices = {
// 协议版本
protocol: {
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3'
},
// 密码套件
ciphers: {
useStrong: true,
forwardSecrecy: true,
avoidWeak: ['RC4', '3DES', 'MD5']
},
// 证书管理
certificates: {
useCA: true,
renewAutomatically: true,
monitorExpiry: true,
useOCSP: true
},
// 安全头
securityHeaders: {
hsts: true,
hstsIncludeSubDomains: true,
hstsPreload: true
},
// 性能
performance: {
sessionReuse: true,
hstsPreload: true,
ocspStapling: true
}
};
总结
HTTPS 和 TLS 配置是现代 Web 应用安全的关键。重要要点包括:
- 始终使用最新的 TLS 版本
- 配置安全的密码套件
- 实施 HSTS
- 使用有效的证书
- 监控证书到期时间
- 实现自动续订
- 定期审核安全配置
- 记录和监控 TLS 连接