如果您正在存储敏感数据——用户个人身份信息(PII)、API密钥、财务记录——那么AES加密就是您需要的工具。它是静态数据对称加密的行业标准:速度快、经过充分验证,并且在所有主流编程语言中都原生支持。
但错误使用AES比无用更糟糕。ECB模式会泄露数据模式。在GCM模式下重复使用IV会完全破坏其安全性保证。而将密钥与密文存储在一起则彻底失去了加密的意义。
以下是您在生产环境中正确使用AES所需的内容。
什么是AES?
AES(高级加密标准)是一种对称分组密码。对称意味着相同的密钥用于加密和解密。分组密码意味着它以固定大小的128位数据块进行操作。
AES于2001年取代DES,如今它无处不在:TLS、磁盘加密、密码管理器、数据库字段加密。与非对称加密(如RSA、ECDH)不同,AES足够快,可以实时加密大量数据。其代价是双方必须共享同一个密钥,因此密钥分发成为您的问题。
AES-128与AES-256:选择哪种密钥长度?
AES有三种密钥长度:128位、192位和256位。在实际应用中,您通常会在128位和256位之间进行选择。
| AES-128 | AES-256 | |
|---|---|---|
| 密钥长度 | 128位 | 256位 |
| 轮数 | 10 | 14 |
| 表现 | 更快 | 约慢40% |
| 安全 | 安全——目前没有已知的实际攻击方法 | 更安全——对未来量子计算机具有抗性 |
| 结论 | 适用于大多数应用场景 | 用于高敏感数据或长期保留的数据 |
AES-128尚未被破解。目前不存在实际攻击方法。但如果您正在加密需要在20年以上保持安全的数据,或者您的威胁模型包括未来量子计算机,那么AES-256提供了有意义的安全余量。对于当前大多数数据库字段加密应用,AES-128是足够的。如有疑问,请使用AES-256;在典型工作负载下,性能损失可以忽略不计。
操作模式:为何选择GCM而非ECB
AES单独只能加密一个128位的数据块。对于真实数据,您需要一种操作模式来正确处理多个数据块并按顺序排列。这一选择至关重要。
| 模式 | 认证 | 需要IV | 结论 |
|---|---|---|---|
| ECB | 不 | 不 | 永远不要使用——相同的明文会产生相同的密文,会泄露数据模式 |
| CBC | 不 | 是的 | 已过时——没有认证功能,易受填充预言攻击 |
| GCM | 是(AEAD) | 是(nonce) | 使用此模式——一次加密并认证 |
使用GCM。GCM(伽罗瓦/计数器模式)是一种AEAD密码——带关联数据的认证加密。它同时完成两项任务:
- 加密您的数据,使其在没有密钥的情况下无法被读取
- 生成一个认证标签,以立即检测任何篡改行为
如果没有认证,攻击者可以修改密文中的位,而您将解密出垃圾数据却毫无察觉。在GCM模式下,如果密文被篡改,解密过程会立即失败,而不会返回任何明文数据。
IV/Nonce:随机且永不重复
GCM需要一个初始化向量(IV),也称为“一次性数字”(nonce)。IV不需要保密,但必须满足以下条件:
- 随机的 —— 使用密码学安全的随机数生成器生成
- 独特的 —— 与相同密钥永不重复使用
在GCM模式下,使用相同密钥和IV重复加密是灾难性的。如果攻击者看到两个使用相同密钥和IV加密的密文,他们可以将这两个密文进行异或运算,从而恢复出两个明文。这不是理论上的问题——它已经在生产系统中发生过。
每次加密操作使用96位(12字节)的随机IV。由于您需要它来进行解密,因此应将其与密文一起存储——将IV置于密文之前是传统格式。
工作代码:AES-256-GCM
以下是生产级别的代码片段。这些代码将IV和认证标签与密文一起存储,以确保解密过程的自包含性。
Node.js
const crypto = require('crypto');
const ALGORITHM = 'aes-256-gcm';
const KEY_LENGTH = 32; // 256 bits
const IV_LENGTH = 12; // 96 bits — recommended for GCM
const TAG_LENGTH = 16; // 128-bit auth tag
function encrypt(plaintext, key) {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
const encrypted = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final(),
]);
const tag = cipher.getAuthTag();
// Layout: [iv (12)] + [tag (16)] + [ciphertext]
return Buffer.concat([iv, tag, encrypted]).toString('base64');
}
function decrypt(ciphertextB64, key) {
const data = Buffer.from(ciphertextB64, 'base64');
const iv = data.subarray(0, IV_LENGTH);
const tag = data.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
const encrypted = data.subarray(IV_LENGTH + TAG_LENGTH);
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
decipher.setAuthTag(tag);
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8');
}
// Usage
const key = crypto.randomBytes(KEY_LENGTH); // store this securely
const ciphertext = encrypt('sensitive data here', key);
const plaintext = decrypt(ciphertext, key);
Python
import os, base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
IV_LENGTH = 12 # 96 bits
def encrypt(plaintext: str, key: bytes) -> str:
iv = os.urandom(IV_LENGTH)
aesgcm = AESGCM(key)
# encrypt() appends the 16-byte auth tag automatically
ciphertext = aesgcm.encrypt(iv, plaintext.encode(), None)
return base64.b64encode(iv + ciphertext).decode()
def decrypt(ciphertext_b64: str, key: bytes) -> str:
data = base64.b64decode(ciphertext_b64)
iv = data[:IV_LENGTH]
ciphertext = data[IV_LENGTH:]
aesgcm = AESGCM(key)
return aesgcm.decrypt(iv, ciphertext, None).decode()
# Usage
key = AESGCM.generate_key(bit_length=256) # store this securely
ciphertext = encrypt('sensitive data here', key)
plaintext = decrypt(ciphertext, key)
安装依赖项: pip install cryptography
您可以使用无需任何设置的交互式工具进行AES加密和解密测试—— IO Tools AES加密/解密工具.
密钥管理:最难的部分
您的加密强度取决于密钥管理。最常见的错误包括:
- 密钥存储在与加密数据相同的数据库中 —— 如果攻击者获取了数据库导出,他们将同时获得两者。
- 密钥提交到源代码控制 —— 即使在被忽略的git目录中
.env,旋转密钥变得困难且历史记录会保留。 - 密钥出现在日志中 —— 应用程序日志会被发送到聚合平台。密钥绝不能出现在这些日志中。
加密密钥应存储在何处:
- AWS KMS / Google Cloud KMS / Azure Key Vault —— 带有审计日志和自动轮换的托管密钥存储
- HashiCorp Vault —— 自托管,适用于多云环境
- 环境变量 —— 适用于低敏感度工作负载且暴露面有限的情况
对于数据库字段加密,环境加密是标准模式:为每条记录或每列生成一个数据加密密钥(DEK),然后使用存储在KMS中的主密钥对DEK进行加密。您的数据库中只包含加密后的DEK和密文——主密钥永远不会进入数据库。
何时使用AES——以及何时不使用
AES适用于以下场景:
- 数据库字段加密 —— 社会安全号码(SSN)、卡号、健康记录
- 静态文件加密 在云存储之前
- 服务间共享密钥 这些服务共享预先建立的密钥
AES不适用于以下场景:
- 在网络上传输数据 —— 使用TLS。不要自行构建传输层。
- 需要非对称加密 —— 如果发送方和接收方无法预先共享密钥,则使用RSA或ECDH进行密钥交换。
- 存储密码 —— 使用bcrypt、scrypt或Argon2。加密的密码可以被解密;而正确哈希的密码则无法被解密。
- 使用托管解决方案 —— AWS Secrets Manager、Vault等工具可自动处理轮换、访问控制和审计日志。如果这些功能符合您的需求,优先选择这些托管方案而非手动AES。
大多数开发者犯下的错误是将AES视为完整的安全解决方案。它只是一个基础组件。搭配认证模式(如GCM)、每次操作使用唯一的IV以及正确的密钥管理,它将非常有效。如果缺少其中任何一项,您构建的系统看起来安全,实际上却并不安全。
