加密与数据安全

What — 是什么

加密与数据安全是 Node.js 服务端保护敏感数据的核心能力,涵盖 crypto 模块的使用、对称/非对称加密、哈希签名、HTTPS/TLS 配置和密钥管理。

核心概念:

  • 对称加密:加解密使用同一密钥,速度快,适合大数据量加密(AES-256-GCM)
  • 非对称加密:公钥加密、私钥解密,或私钥签名、公钥验证,速度慢,适合密钥交换和签名(RSA/ECDSA)
  • 哈希:单向不可逆,用于密码存储和数据完整性校验(bcrypt/scrypt/SHA-256)
  • 数字签名:私钥签名 + 公钥验证,确保数据未被篡改且来源可信
  • HTTPS/TLS:传输层加密,防止中间人窃听和篡改
  • 密钥管理:密钥的生成、存储、轮换和撤销策略

关键特性:

  • crypto.createCipheriv() 对称加密,crypto.createDecipheriv() 解密
  • crypto.generateKeyPairSync() 生成 RSA/ECDSA 密钥对
  • crypto.createSign() 签名,crypto.createVerify() 验证
  • bcrypt.hash() 密码哈希(自动加盐),bcrypt.compare() 验证
  • https.createServer() 创建 HTTPS 服务器

Why — 为什么

适用场景:

  • 密码存储:哈希 + 盐,不可逆
  • 数据加密:敏感字段(身份证/银行卡)入库前加密
  • API 签名验证:确保请求未被篡改
  • HTTPS 配置:传输层加密
  • JWT 签名:确保 Token 不被伪造

对比加密方案:

维度对称加密(AES)非对称加密(RSA)哈希(bcrypt)
方向双向(加解密)双向(公私钥)单向(不可逆)
速度
密钥1个共享密钥公钥+私钥无密钥(用盐)
适用大数据加密密钥交换/签名密码存储

优缺点:

  • ✅ 优点:
    • AES-GCM 提供加密+完整性校验
    • bcrypt 自适应加盐,抗暴力破解
    • Node.js crypto 模块功能完善
  • ❌ 缺点:
    • 密钥管理复杂,泄露等于防线失守
    • 非对称加密速度慢,不适合大数据
    • 加密增加系统复杂度和性能开销

How — 怎么用

快速上手

const crypto = require('crypto');
const bcrypt = require('bcrypt');

// 密码哈希
const hash = await bcrypt.hash('password123', 12);
const match = await bcrypt.compare('password123', hash);

// 对称加密
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const encrypted = Buffer.concat([cipher.update('secret data', 'utf8'), cipher.final()]);
const authTag = cipher.getAuthTag();

代码示例

对称加密 + 非对称加密 + 签名:

const crypto = require('crypto');

// 对称加密(AES-256-GCM)
function encrypt(text, key) {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
    const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
    const authTag = cipher.getAuthTag();
    return { iv: iv.toString('hex'), encrypted: encrypted.toString('hex'), authTag: authTag.toString('hex') };
}

function decrypt(encData, key) {
    const decipher = crypto.createDecipheriv(
        'aes-256-gcm', key,
        Buffer.from(encData.iv, 'hex')
    );
    decipher.setAuthTag(Buffer.from(encData.authTag, 'hex'));
    const decrypted = Buffer.concat([
        decipher.update(Buffer.from(encData.encrypted, 'hex')),
        decipher.final()
    ]);
    return decrypted.toString('utf8');
}

// 非对称加密(RSA)
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
    modulusLength: 2048,
    publicKeyEncoding: { type: 'spki', format: 'pem' },
    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});

// 公钥加密、私钥解密
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from('secret message'));
const decrypted = crypto.privateDecrypt(privateKey, encrypted);

// 私钥签名、公钥验证
const sign = crypto.createSign('SHA256');
sign.update('data to sign');
const signature = sign.sign(privateKey, 'hex');

const verify = crypto.createVerify('SHA256');
verify.update('data to sign');
const isValid = verify.verify(publicKey, signature, 'hex');

HTTPS 服务器配置:

const https = require('https');
const fs = require('fs');

const options = {
    key: fs.readFileSync('server-key.pem'),
    cert: fs.readFileSync('server-cert.pem'),
    ca: fs.readFileSync('ca-cert.pem'),
    minVersion: 'TLSv1.2',
    ciphers: 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256',
    honorCipherOrder: true
};

const server = https.createServer(options, app);
server.listen(443);

// HTTP → HTTPS 重定向
const http = require('http');
http.createServer((req, res) => {
    res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
    res.end();
}).listen(80);

常见问题与踩坑

问题原因解决方案
IV 重复导致密文可预测每次加密必须用随机 IVcrypto.randomBytes(16) 生成
密钥硬编码在代码中泄露风险环境变量或密钥管理服务
GCM authTag 校验失败数据被篡改或 IV/Key 不匹配检查传输完整性
bcrypt 哈希耗时故意慢(抗暴力破解)调整 cost factor(10-12 合理)
TLS 1.0/1.1 不安全已弃用的协议版本minVersion: 'TLSv1.2'

最佳实践

  • 密码存储用 bcrypt/scrypt,不用 MD5/SHA
  • 对称加密用 AES-256-GCM(含完整性校验)
  • 密钥从环境变量或密钥管理服务加载,不硬编码
  • HTTPS 强制 TLS 1.2+,禁用弱密码套件
  • IV 每次加密随机生成,与密文一起存储
  • 密钥定期轮换,旧密钥安全销毁

面试题

Q1: 对称加密和非对称加密的区别?

对称加密:加解密用同一密钥,速度快,适合大数据。常用 AES-256-GCM。非对称加密:公钥加密私钥解密(或反向),速度慢(比 AES 慢 1000 倍+),适合密钥交换和签名。常用 RSA-2048/ECDSA。实际混合使用:非对称加密交换对称密钥,再用对称密钥加密数据(HTTPS/TLS 就是这个模式)。

Q2: 为什么密码存储用 bcrypt 而不是 SHA-256?

三个原因:① bcrypt 自适应加盐——每个密码自动生成独立盐值,无需额外存储;② bcrypt 可调节计算成本——cost factor 控制哈希迭代次数,硬件升级后提高 cost 保持抗暴力破解能力;③ bcrypt 设计目标就是慢——SHA-256 设计目标是快(用于签名校验),快意味着暴力破解也快。bcrypt 的慢是故意的设计,10 次迭代约 100ms,SHA-256 单次微秒级。

Q3: AES-GCM 中的 AuthTag 是什么?

AuthTag(认证标签)是 GCM 模式提供的完整性校验值(16 字节)。加密时 GCM 同时计算密文和认证标签;解密时重新计算标签并与附带的标签比对,不匹配则说明数据被篡改。这是 GCM 相比 CBC 模式的核心优势——CBC 只加密不认证,需要额外的 HMAC 做完整性校验(Encrypt-then-MAC),GCM 一步完成。没有 AuthTag 的密文无法验证完整性。

Q4: HTTPS/TLS 握手过程?

TLS 1.3 握手(简化):① ClientHello——客户端发送支持的 TLS 版本和密码套件;② ServerHello——服务端选择版本和套件,发送证书;③ 密钥交换——客户端验证证书,生成预主密钥,用服务端公钥加密发送(或用 ECDHE 计算共享密钥);④ 完成——双方用预主密钥派生会话密钥,后续通信用对称加密。TLS 1.3 将握手从 2-RTT 缩短到 1-RTT,0-RTT 模式用于重连。


相关链接: