必须使用 attr_encrypted 指定 iv 错误
must specify an iv error using attr_encrypted
我正在尝试将加密的 jsonb 字段添加到 rails 中的用户模型。我在尝试读取或设置值时遇到错误。
错误
irb(main):002:0> User.last.q2_email_address = "bob@bob.com"
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT [["LIMIT", 1]]
Encrypt Data Key (190.1ms) Context: {:model_name=>"User", :model_id=>11}
/Users/antarr/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/encryptor-3.0.0/lib/encryptor.rb:61:in `crypt': must specify an iv (ArgumentError)
user.rb
has_kms_key eager_encrypt: :fetch_id
attr_encrypted :settings, key: :kms_key, mashall: true
store :settings,
accessors: [:address1, :address2, :city, :customer_id, :customer_name, :customer_primary_cif, :social_security_number,
:email_address, :first_name, :group_desc, :group_id, :home_phone, :aba, :hq_session_id, :language, :last_name,
:login_name, :middle_name, :mobile_phone, :postal_code, :ssn, :state, :user_logon_id, :user_id, :user_primary_cif,
:work_phone, :ip_address, :token], coder: JSON, prefix: :q2
迁移
def up
add_column :users, :encrypted_kms_key, :text
add_column :users, :encrypted_settings, :jsonb, null: false, default: '{}'
add_column :users, :encrypted_settings_iv, :string
add_index :users, :encrypted_settings, using: :gin
end
config/initializer/aws.rb
Aws.config[:credentials] = Aws::Credentials.new(
ENV['AMAZON_ACCESS_KEY'],
ENV['AMAZON_SECRET_KEY']
)
宝石文件
gem 'attr_encrypted' # 3.1.0
gem 'aws-sdk-kms'
gem 'kms_encrypted'
gemattr_encrypted
中有2个方法encrypt
,一个是class方法,一个是实例方法,实例方法会自动生成random iv
,同时class方法不会,它使用你设置的iv
。
调用User.last.q2_email_address = "bob@bob.com"
时,this method会调用class方法encrypt
,不会调用实例方法,所以如果不设置iv
,会报错must specify an iv (ArgumentError)
。
有两种方法可以修复,第一种是设置 iv
attr_encrypted :settings, key: :kms_key, mashall: true, iv: SecureRandom.random_bytes(12)
不幸的是,将引发另一个错误(与 iv_len
相关),我仍然没有找出根本原因并修复它。
第二种方式:将this method复制到模型中,这样就会调用实例方法encrypt
,随机生成一个iv
,就可以了。
但是,在store attributes
的情况下,它不保存encrypted attribute
,例如
user = User.last
user.settings = {....}
user.save! # OK
user.q2_email_address = "bob@bob.com"
user.settings # changed, but not re-encrypt `encrypted_settings`
user.save! # changed settings will not be saved
所以我想出了一个想法,我们可以创建一个模块支持 store attributes
,如下所示
# model/concern/store_encrypted.rb
module StoreEncrypted
def self.extended(base)
base.class_eval do
# initialize store attributes values with default `{}`
# so that a new object or existed object that miss iv
# will generate random iv
def initialize(*args)
super(*args)
@@store_attr_encryptes.each do |attribute|
instance_variable_get("@#{attribute}") || send("#{attribute}=", {})
end
end
alias old_save! save!
def save!(**options, &block)
# re-set attribute (encrypt again)
@@store_attr_encryptes&.each do |attribute|
send("#{attribute}=", send("#{attribute}"))
end
old_save!(**options, &block)
end
end
end
def store_encrypted(attribute, options={})
@@store_attr_encryptes ||= []
@@store_attr_encryptes << attribute
attr_encrypted attribute, key: options[:key], marshal: options[:marshal]
store attribute, accessors: options[:accessors], coder: options[:coder],
prefix: options[:prefix], suffix: options[:suffix]
# copy
define_method("#{attribute}=") do |value|
send("encrypted_#{attribute}=", encrypt(attribute, value))
instance_variable_set("@#{attribute}", value)
end
end
end
# user.rb
class User < ActiveRecord::Base
extend StoreEncrypted
has_kms_key eager_encrypt: :fetch_id
store_encrypted :settings, key: :kms_key, mashall: true,
accessors: [...], coder: JSON, prefix: :q2
end
#
user = User.last
user.q2_email_address = "bob@bob.com"
user.save! # it'll re-encrypt settings
我正在尝试将加密的 jsonb 字段添加到 rails 中的用户模型。我在尝试读取或设置值时遇到错误。
错误
irb(main):002:0> User.last.q2_email_address = "bob@bob.com"
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT [["LIMIT", 1]]
Encrypt Data Key (190.1ms) Context: {:model_name=>"User", :model_id=>11}
/Users/antarr/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/encryptor-3.0.0/lib/encryptor.rb:61:in `crypt': must specify an iv (ArgumentError)
user.rb
has_kms_key eager_encrypt: :fetch_id
attr_encrypted :settings, key: :kms_key, mashall: true
store :settings,
accessors: [:address1, :address2, :city, :customer_id, :customer_name, :customer_primary_cif, :social_security_number,
:email_address, :first_name, :group_desc, :group_id, :home_phone, :aba, :hq_session_id, :language, :last_name,
:login_name, :middle_name, :mobile_phone, :postal_code, :ssn, :state, :user_logon_id, :user_id, :user_primary_cif,
:work_phone, :ip_address, :token], coder: JSON, prefix: :q2
迁移
def up
add_column :users, :encrypted_kms_key, :text
add_column :users, :encrypted_settings, :jsonb, null: false, default: '{}'
add_column :users, :encrypted_settings_iv, :string
add_index :users, :encrypted_settings, using: :gin
end
config/initializer/aws.rb
Aws.config[:credentials] = Aws::Credentials.new(
ENV['AMAZON_ACCESS_KEY'],
ENV['AMAZON_SECRET_KEY']
)
宝石文件
gem 'attr_encrypted' # 3.1.0
gem 'aws-sdk-kms'
gem 'kms_encrypted'
gemattr_encrypted
中有2个方法encrypt
,一个是class方法,一个是实例方法,实例方法会自动生成random iv
,同时class方法不会,它使用你设置的iv
。
调用User.last.q2_email_address = "bob@bob.com"
时,this method会调用class方法encrypt
,不会调用实例方法,所以如果不设置iv
,会报错must specify an iv (ArgumentError)
。
有两种方法可以修复,第一种是设置 iv
attr_encrypted :settings, key: :kms_key, mashall: true, iv: SecureRandom.random_bytes(12)
不幸的是,将引发另一个错误(与 iv_len
相关),我仍然没有找出根本原因并修复它。
第二种方式:将this method复制到模型中,这样就会调用实例方法encrypt
,随机生成一个iv
,就可以了。
但是,在store attributes
的情况下,它不保存encrypted attribute
,例如
user = User.last
user.settings = {....}
user.save! # OK
user.q2_email_address = "bob@bob.com"
user.settings # changed, but not re-encrypt `encrypted_settings`
user.save! # changed settings will not be saved
所以我想出了一个想法,我们可以创建一个模块支持 store attributes
,如下所示
# model/concern/store_encrypted.rb
module StoreEncrypted
def self.extended(base)
base.class_eval do
# initialize store attributes values with default `{}`
# so that a new object or existed object that miss iv
# will generate random iv
def initialize(*args)
super(*args)
@@store_attr_encryptes.each do |attribute|
instance_variable_get("@#{attribute}") || send("#{attribute}=", {})
end
end
alias old_save! save!
def save!(**options, &block)
# re-set attribute (encrypt again)
@@store_attr_encryptes&.each do |attribute|
send("#{attribute}=", send("#{attribute}"))
end
old_save!(**options, &block)
end
end
end
def store_encrypted(attribute, options={})
@@store_attr_encryptes ||= []
@@store_attr_encryptes << attribute
attr_encrypted attribute, key: options[:key], marshal: options[:marshal]
store attribute, accessors: options[:accessors], coder: options[:coder],
prefix: options[:prefix], suffix: options[:suffix]
# copy
define_method("#{attribute}=") do |value|
send("encrypted_#{attribute}=", encrypt(attribute, value))
instance_variable_set("@#{attribute}", value)
end
end
end
# user.rb
class User < ActiveRecord::Base
extend StoreEncrypted
has_kms_key eager_encrypt: :fetch_id
store_encrypted :settings, key: :kms_key, mashall: true,
accessors: [...], coder: JSON, prefix: :q2
end
#
user = User.last
user.q2_email_address = "bob@bob.com"
user.save! # it'll re-encrypt settings