Ruby on Rails数据库层数据加密存储实现方法及gem推荐
Rails 5.1.7 + Ruby 2.6.3 环境下数据库层敏感字段加密实现方案
你当前使用的Rails版本没有内置Active Record字段加密能力(该特性从Rails 7才开始内置),Ruby 2.6.3也不兼容高版本加密类库的语法要求,不建议自己手写加密逻辑,很容易出安全问题或者加解密不一致的bug,优先选成熟的兼容版本gem实现即可。
选型推荐
- 首选
attr_encryptedgem,必须锁定3.1.0版本,这个版本是最后一个兼容Ruby 2.6.x、Rails 5.1.x的稳定版,社区迭代超过10年,透明加解密逻辑不需要改动现有业务读写逻辑,踩坑资料全。 - 不要安装4.0及以上版本的attr_encrypted,高版本最低要求Ruby 2.7、Rails 6.0,你的环境会直接装依赖失败。
- 禁止自己手写AES加密逻辑,90%以上的自实现方案会漏IV随机化、密文编码、填充模式校验的处理,要么出现密文截断存库失败,要么相同明文生成相同密文留下安全隐患。
落地步骤
配置依赖
在项目Gemfile中添加如下配置后执行bundle install:gem 'attr_encrypted', '3.1.0'调整数据库表结构
加密后的密文长度远大于原明文,存储字段必须用text类型,不要用默认的string(255字符上限很容易截断密文导致解密失败)。以用户表存手机号为例,新建迁移文件:class AddEncryptedPhoneToUsers < ActiveRecord::Migration[5.1] def change # 存加密后的密文,字段名必须遵循encrypted_<属性名>的约定 add_column :users, :encrypted_phone, :text # 存每个记录独立的加密偏移量IV,避免相同手机号生成相同密文 add_column :users, :encrypted_phone_iv, :string end end执行
rails db:migrate完成表结构变更。配置模型加密规则
在对应模型中添加加密配置,注意加密密钥绝对不能硬编码在代码里,要存在环境变量或者Rails加密凭据中:class User < ApplicationRecord attr_encrypted :phone, key: ENV['PHONE_ENCRYPT_KEY'], algorithm: 'aes-256-cbc', encode: true, # 自动将二进制密文转成可存储的字符串格式,避免编码兼容问题 default_iv: false # 强制每条记录使用独立随机IV,消除相同明文生成相同密文的安全风险 end加密密钥可以直接在rails console中执行
SecureRandom.hex(16)生成,会返回一个32位长度的随机字符串,符合AES-256的密钥长度要求,生成后妥善备份,密钥丢失后所有历史加密数据将永久无法解密。
使用注意事项
- 配置完成后业务层不需要改动原有逻辑,正常读写
phone字段即可,gem会自动在写入数据库时完成加密、查询读取时完成解密:user = User.create(phone: '13800138000') # 数据库中encrypted_phone字段存的是不可读的密文,无法直接反推明文 User.last.phone # 直接返回解密后的明文手机号"13800138000" - 不要直接给
encrypted_phone字段加数据库唯一索引,因为每条记录的IV是随机的,就算手机号相同,生成的密文也完全不同,索引不会生效。如果需要做手机号唯一校验,可以额外新增一个手机号的加盐哈希字段,对哈希字段加唯一索引即可。 - 密文不支持数据库层面的模糊检索,如果需要按手机号搜索,数据量小的场景可以先拉取数据解密后在内存过滤,数据量大的场景需要额外搭密文检索引擎,普通业务场景不建议做这类高成本实现。
内容的提问来源于stack exchange,提问作者user18578733
相关产品推荐
相关产品推荐

