Skip to content
返回质量保障

加密最佳实践

正确用加密: 对的算法、对的模式、对的密钥管理——绝不自己造。

为什么

加密是少数"能用"和"安全"天差地别的领域。自造加密函数产正确解密的密文且看起来加密了,但任何知模式者秒破。用 AES-ECB(不安全模式)产正确解密但泄露明文模式的密文——著名 ECB 企鹅。用快哈希(MD5、SHA1)存密码让攻击者 GPU 上每秒暴力数十亿。唯一安全方式是用久审库、密码学家推荐的算法和模式、绝不定制。库是对的;危险在从库菜单选错选项。

何时使用

每当加密、哈希、签名或生成随机值。安全审计时审所有加密用法。若你从头写加密代码(非调库),停下调库。

如何做

1. 密码: 用慢、加盐、内存硬哈希
   - bcrypt(cost ≥ 12)、argon2id(首选)、scrypt——绝不用 MD5、SHA1、SHA256
   - 慢是特性——使暴力不可行
   - 哈希+盐+参数一起存(bcrypt 编码三者)

2. 对称加密: AES-256 GCM 模式(认证加密)
   - GCM 提供机密性和完整性(篡改密文被检测)
   - 绝不用 ECB(泄露模式)或无 MAC 的 CBC(可塑)
   - 每次加密用唯一 IV(GCM 生成;绝不重用)

3. 非对称: 用库高层 API 非原语
   - 传输用 TLS(绝不自己实现)、封装/开箱用 libsodium
   - 加密用 RSA-OAEP、签名用 RSA-PSS 或 Ed25519——绝不用 PKCS1v1.5
   - 用 2048+ 位 RSA 或 256 位曲线(Ed25519、X25519)密钥

4. 随机: 用密码安全 PRNG,绝不用 Math.random()
   - crypto.randomBytes()(Node)、secrets 模块(Python)、getrandom(Go)
   - Math.random() 可预测,绝不用作令牌、ID 或密钥

5. 密钥管理: 绝不硬编码、轮换、按用途分
   - 每用途一密钥(加密、签名、API 认证)——绝不重用密钥
   - 存密钥管理器(Vault、KMS),按计划轮换
   - 若需从一主密钥派多密钥用 HKDF 派每记录密钥

示例

// ❌ 错——自造,秒破
function encrypt(secret, key) {
  return secret.split("").map(c =>
    String.fromCharCode(c.charCodeAt(0) + key)  // 凯撒密码——非加密
  ).join("");
}

// ❌ 错——ECB 泄露明文模式
import crypto from "crypto";
const ct = crypto.createCipheriv("aes-256-ecb", key, null).update(pt);

// ❌ 错——MD5 GPU 上每秒暴力数十亿
const hash = crypto.createHash("md5").update(password).digest("hex");

// ✅ 对——密码用 bcrypt、数据用 AES-256-GCM
import bcrypt from "bcrypt";
import crypto from "crypto";

// 密码哈希——慢、加盐、内存硬
const hash = await bcrypt.hash(password, 12);          // cost 因子 12
const valid = await bcrypt.compare(password, hash);     // 常时比较

// 对称加密——认证、每消息唯一 IV
const iv = crypto.randomBytes(12);                       // GCM 96 位 IV
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ct = Buffer.concat([cipher.update(pt), cipher.final()]);
const tag = cipher.getAuthTag();                         // 完整性标签
// 存 iv + tag + ct;解密验标签后返明文。

// 随机令牌——密码安全
const token = crypto.randomBytes(32).toString("hex");   // 非 Math.random()

常见问题

  • ⚠用快哈希(MD5、SHA1、SHA256)存密码——这些为速度设计(文件校验和),带 GPU 攻击者每秒暴力数十亿,8 字符密码空间变分钟。
  • ⚠用 AES ECB 模式——ECB 独立加密每块,相同明文块产相同密文块,泄露明文结构(ECB 企鹅图是经典演示)。
  • ⚠同密钥跨 GCM 加密重用 IV/nonce 毁安全保证——重用 nonce 的 GCM 非仅弱而是灾难性破(密钥可恢复)。
  • ⚠安全敏感值(会话令牌、密码重置码)用 Math.random()——Math.random() 是为速度设计的 PRNG 非不可预测,输出可预测。

最佳实践

  • ✓密码始终用 bcrypt(cost ≥ 12)、argon2id 或 scrypt——这些故意慢且内存硬,使 GPU 暴力不可行,且编码盐和参数与哈希。
  • ✓对称加密始终用 AES-256-GCM(或 libsodium secretbox)——认证加密检测篡改,GCM 唯一 IV 要求防灾难性重用攻击。
  • ✓随机值始终用 CSPRNG(crypto.randomBytes、secrets 模块)——会话令牌用 Math.random() 的代价是可预测令牌和被入侵会话。
  • ✓绝不自己实现加密原语——用 libsodium、Tink 或语言标准加密库,用其高层 API(seal/box、sign)非原始原语。