使用 attr_encrypted 和 rails 7

问题描述 投票:0回答:2

我们已经将我们的应用程序升级到 Rails 7,现在我们遇到了 attr_encrypted gem 的问题。当我们尝试启动 Rails 服务器/控制台时,我们收到以下错误:

gems/ruby-3.0.2/gems/attr_encrypted-3.1.0/lib/attr_encrypted.rb:176:in `block in attr_encrypted': undefined method `[]=' for nil:NilClass (NoMethodError)

如果我们恢复到 Rails 6,错误就会消失,有没有人遇到过同样的问题?

ruby-on-rails ruby ruby-on-rails-7 attr-encrypted
2个回答
7
投票

所以我终于明白是怎么回事了。

Rails 7.0 有它自己的加密,attr_encrypted gem 和 rails 7 都引用变量

encrypted_attributes
并且 rails 7 变量优先使 gem 无用。 attr_encrypted gem 上有一个 PR 可以解决这个问题,但是这个 gem 已经很多年没有更新了,我怀疑现在会更新。

GoRails 发布了一个教程,介绍如何将数据从使用 attr_encrypted 迁移到使用 rails 7 加密。我不想为此付费,所以我查看了教程的 git repo。

他们所做的是在迁移中自行解密数据并手动更新新的加密字段。

这是他们迁移的链接。 https://github.com/gorails-screencasts/migrate-attr_encrypted-to-rails-7-encryption/blob/master/db/migrate/20211005214633_migrate_encrypted_attributes.rb


0
投票

根据之前的回答,双向完全迁移:

class SwitchToRailsBuiltInEncryption < ActiveRecord::Migration[7.0]

  ATTRIBUTES = [
    [Account, :password],
    [Account, :client_secret],
  ]

  def up
    ATTRIBUTES.each do |klass, attribute|
      add_column klass.table_name, attribute, :string, length: 510

      klass.all.each do |record|
        puts "Processing #{klass} with ID #{record.id}"
        if record.send("encrypted_#{attribute}").present?
          encrypted_value = record.send("encrypted_#{attribute}")
          iv = record.send("encrypted_#{attribute}_iv")
          value = decrypt(encrypted_value, iv)
          record.update!(attribute => value)
        end
      end

      remove_column klass.table_name, "encrypted_#{attribute}"
      remove_column klass.table_name, "encrypted_#{attribute}_iv"
    end
  end

  def down
    ATTRIBUTES.each do |klass, attribute|
      add_column klass.table_name, "encrypted_#{attribute}", :string, length: 510
      add_column klass.table_name, "encrypted_#{attribute}_iv", :string, length: 510

      klass.all.each do |record|
        puts "Processing #{klass} with ID #{record.id}"
        if record.send(attribute).present?
          encrypted_value, iv = encrypt(record.send(attribute))
          record.update!("encrypted_#{attribute}" => encrypted_value, "encrypted_#{attribute}_iv" => iv)
        end
      end

      remove_column klass.table_name, attribute
    end
  end

  # based on https://github.com/gorails-screencasts/migrate-attr_encrypted-to-rails-7-encryption/blob/master/db/migrate/20211005214633_migrate_encrypted_attributes.rb
  # as suggested by https://stackoverflow.com/questions/72096672/using-attr-encrypted-with-rails-7
  def decrypt(encrypted_value, iv)
    value = Base64.decode64(encrypted_value)

    cipher = OpenSSL::Cipher.new("aes-256-gcm")
    cipher.decrypt

    cipher.key = Rails.application.credentials.key
    cipher.iv = Base64.decode64(iv)

    cipher.auth_tag = value[-16..]
    cipher.auth_data = ""

    cipher.update(value[0..-17]) + cipher.final
  end

  def encrypt(value)
    cipher = OpenSSL::Cipher.new("aes-256-gcm")
    cipher.encrypt

    cipher.key = Rails.application.credentials.key
    iv = cipher.random_iv

    cipher.auth_data = ""

    encrypted_value = cipher.update(value) + cipher.final

    return Base64.encode64(encrypted_value + cipher.auth_tag), Base64.encode64(iv)
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.