用途
本技能用于帮助开发者在静态存储与传输过程中加密个人数据(PII),以符合 PDPA 2024 的合规要求。资料指出,如果数据库发生泄露但 PII 字段已加密,开发者可以据此对抗疏忽指控。
需要加密的字段
根据 PDPA 2024,以下字段属于 data peribadi(个人数据),应进行静态加密:
- IC number (NRIC):唯一标识符,存在身份盗用风险
- Phone number:直接联系方式,存在骚扰风险
- Home address:存在人身安全风险
- Bank account / card numbers:存在金融欺诈风险
- Medical records:存在歧视风险
- Biometric data:一旦泄露无法更改
- Email(部分场景):存在钓鱼与账户接管风险
实现模式
资料提供了四种语言/平台的实现示例,代码内容如下。
Node.js / TypeScript
使用 crypto 模块的 createCipheriv、createDecipheriv、randomBytes,算法为 aes-256-gcm,密钥为 32 字节(hex)。加密结果以 ciphertext、iv、tag 三个 hex 字段返回,数据库写入时分别存入对应列。
Cloudflare Workers (Web Crypto API)
使用 AES-GCM(length 256),通过 crypto.subtle.importKey 导入密钥,crypto.getRandomValues 生成 12 字节 IV,将 IV 与密文打包后以 base64 返回;解密时从打包数据中切出前 12 字节作为 IV。
Python
提供两种方式:Fernet(更简单,包含时间戳)和 AES-256-GCM(更可控)。Fernet 使用 base64 编码的 32 字节密钥;AESGCM 使用 hex 编码的 32 字节密钥,生成 12 字节 nonce,将 nonce 与密文拼接后 base64 编码。
Laravel
利用内置的 APP_KEY 加密。在模型中通过 $casts 将 ic_number、phone、address 设为 'encrypted' 实现自动加解密,也可用 Crypt::encryptString 和 Crypt::decryptString 手动调用。
密钥管理规则
- 正确生成密钥:
openssl rand -hex 32 - 密钥与数据分开存储:如果数据库被攻破,密钥不应在同一位置
- 定期轮换密钥并支持多个活跃密钥(current 与 previous),previous 用于解密旧数据
- 绝不硬编码密钥:始终使用环境变量或密钥管理器
- 大数据集使用信封加密:用数据密钥加密数据,再用主密钥加密数据密钥
数据库表结构模式
资料给出的 users 表包含:id、name、email,以及带 _enc 后缀的加密 PII 字段(ic_number_enc、phone_enc、address_enc),并包含 encryption_key_version 字段(默认 1)以支持密钥轮换,以及 created_at 时间戳。仍可通过未加密字段进行搜索;对于需要搜索的加密字段,可存储盲索引(HMAC 哈希)。
加密检查清单
- 所有 PII 字段静态加密(IC、电话、地址、银行信息)
- 使用 AES-256-GCM(带认证的加密)
- 每次加密操作使用唯一 IV/nonce(绝不重用)
- 加密密钥存储在环境变量中,不在代码中
- 已建立密钥轮换策略
- 密钥与加密数据分开存储
- 所有传输中的数据强制 HTTPS
- 数据库备份同样加密
PDPA 2024 后果
资料引用 Seksyen 5(1) dan 5(2) PDPA 2010 (Pindaan 2024):最高罚款 RM1,000,000,最高监禁 3 年。PDPA 2024 明确要求采取 langkah keselamatan yang munasabah(合理安全措施)保护个人数据;静态加密被视为基线合理措施。如果发生泄露且数据以明文存储,开发者无法主张已采取合理预防措施。