SuperAPI AI 工具集

active-record-encryption

编程与开发

查看项目

更新于 10-15 13:49

技能介绍

用途

本技能用于在 Rails 应用中,通过内置的 Active Record Encryption 在应用层加密敏感数据。适用于为模型属性添加 encrypts 声明、首次配置加密密钥、将已有未加密数据迁移为加密数据、轮换密钥、查询确定性加密属性、加密 Action Text 内容,以及调试解密失败、查询不匹配等加密相关错误。

核心原则

  • 只加密真正需要加密的字段——加密会增加复杂度和存储开销,需谨慎决定。
  • 仅在必须查询时使用确定性加密——非确定性更安全,应作为默认选择。
  • 密钥属于 credentials,不属于代码——绝不提交加密密钥。
  • 加密前先规划迁移——已有未加密数据需要迁移策略。
  • 测试时启用加密——fixtures 需要 encrypt_fixtures: true,否则测试会失败。

决策矩阵

  • 需要查询/find_by 该字段?是 → deterministic: true;否 → 默认(非确定性)。
  • 需要唯一性校验?是 → deterministic: true(若需忽略大小写,加 downcase/ignore_case)。
  • 需要保留大小写但按大小写不敏感查询?是 → deterministic: true, ignore_case: true(会新增 original_ 列)。
  • 仅保护静态数据(日志、备份)?是 → 默认(非确定性)即可。

主要能力与使用方式

1. 生成并存储密钥

运行 bin/rails db:encryption:init,将输出的三个值存入 credentials(bin/rails credentials:edit):

active_record_encryption:
  primary_key: ...
  deterministic_key: ...
  key_derivation_salt: ...

也可使用环境变量(12-factor 应用):

config.active_record.encryption.primary_key = ENV["ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY"]
config.active_record.encryption.deterministic_key = ENV["ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY"]
config.active_record.encryption.key_derivation_salt = ENV["ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT"]

验证:bin/rails runner "puts ActiveRecord::Encryption.config.primary_key.present?" 应输出 true。

2. 检查现有模式

搜索代码库中已有的加密声明与配置,检查计划加密字段的列大小,并沿用项目现有约定(环境变量或 credentials)。

3. 声明加密属性

非确定性(默认,最安全):

class User < ApplicationRecord
  encrypts :ssn
  encrypts :medical_notes
end

确定性(可查询):

class User < ApplicationRecord
  encrypts :email, deterministic: true
  encrypts :phone, deterministic: true, downcase: true
end

Action Text:

class Message < ApplicationRecord
  has_rich_text :content, encrypted: true
end

序列化属性(顺序重要):

class Article < ApplicationRecord
  serialize :metadata, type: Hash    # 先
  encrypts :metadata                  # 后
end

4. 处理列大小

加密后数据更大:string(255) ASCII 需 string(510);string(255) Unicode 需 string(1275);string(500) 非西方字符需 string(2255);text 无需变更。列过小需生成迁移调整。

5. 迁移已有未加密数据

分三阶段:

  1. 启用共存:config.active_record.encryption.support_unencrypted_data = true 与 extend_queries = true。
  2. 加密已有记录(任务或迁移中 User.find_each(&:encrypt))。
  3. 验证全部加密后,移除共存配置。

6. 配置密钥轮换(仅非确定性)

确定性加密不支持密钥轮换。非确定性可将新密钥加入列表(最后一个用于加密,所有密钥用于解密),重新加密已有数据后移除旧密钥。

7. 配置测试环境

在 config/environments/test.rb 中设置 config.active_record.encryption.encrypt_fixtures = true。迁移数据时同样启用共存配置。

8. 正确查询

确定性字段可正常查询;非确定性字段无法查询(每次密文不同)。若需按非确定性字段查找,可改用确定性(安全性降低)或另加确定性摘要/哈希列。

9. 唯一性与大小写

唯一性校验需要确定性加密。不要在校验上使用 case_sensitive: false,而应在 encrypts 上使用 downcase: true 或 ignore_case: true。ignore_case: true 需要额外的 original_<column_name> 列。

关键错误与修复

  • Errors::Configuration:密钥未配置 → 运行 rails db:encryption:init 并存入 credentials。
  • Errors::Decryption:密钥错误或数据损坏 → 检查密钥配置;迁移期间启用 support_unencrypted_data。
  • 查询已有数据返回 nil:查询了非确定性字段 → 添加 deterministic: true。
  • undefined column 'original_X':使用 ignore_case: true 但未迁移 → 添加 original_<column> 列。
  • Fixture 测试失败:fixtures 未加密 → 测试配置中设置 encrypt_fixtures = true。
  • 序列化属性静默失效:声明顺序错误 → serialize 放在 encrypts 之前。

适用场景

适用于 Rails 应用中需要保护 PII、财务数据、健康信息等敏感字段的场景,包括新增加密、密钥配置、数据迁移、密钥轮换、查询调试与测试配置。

限制与注意事项

  • 确定性加密安全性低于非确定性,且不支持密钥轮换。
  • 非确定性加密字段无法直接查询。
  • 加密会增加存储开销,需调整列大小。
  • 密钥必须存入 credentials 或真实环境变量,不得写入仓库。
  • 不要加密所有字段,仅加密真正敏感的字段。
  • 迁移已有数据时必须先启用共存配置,否则读取会崩溃。