Skip to content
On this page

HTTPS 与 TLS

HTTPS (HTTP Secure) 和 TLS (Transport Layer Security) 是保护网络通信安全的关键技术,它们通过加密传输数据来防止窃听、篡改和冒充攻击。

HTTPS 与 TLS 基础概念

什么是 HTTPS

HTTPS 是 HTTP 协议的安全版本,它通过 TLS/SSL 协议对 HTTP 通信进行加密,确保数据在客户端和服务器之间的安全传输。

什么是 TLS

TLS (传输层安全) 是 SSL (安全套接字层) 的后继协议,它提供了通信安全的三个核心要素:

  1. 加密 - 保护数据隐私
  2. 认证 - 验证通信方身份
  3. 完整性 - 确保数据未被篡改

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 应用安全的关键。重要要点包括:

  1. 始终使用最新的 TLS 版本
  2. 配置安全的密码套件
  3. 实施 HSTS
  4. 使用有效的证书
  5. 监控证书到期时间
  6. 实现自动续订
  7. 定期审核安全配置
  8. 记录和监控 TLS 连接