红宝石和activerecord中的AES解密

时间:2016-05-11 20:07:31

标签: ruby activerecord encryption

我有超级丑陋的代码,如下所示:

class User < ActiveRecord::Base
   self.table_name = 'users'
  def get_password
    @test_password = User.find_by_sql "SELECT CAST(AES_DECRYPT(Pass, 'kkk') AS CHAR(50)) Pass From prod.sys_users Where Owner = '"+@owner+"' AND User = '"+@user+"'"
    @test_password[0].Pass
  end
end

此代码有效,但它让我感到恶心,因为它不是根据Ruby编码样式编写的。所以我决定修复这段代码,到目前为止我所拥有的代码:

class User < ActiveRecord::Base
  self.table_name = 'users'
  def get_pass
    User.where(Owner: @owner, User: @user).pluck(:Pass).first
  end
end

所以,我收到加密密码,我该怎么解密呢? 我累了OpenSSL,但此处的密钥'kkk'太短了。 我该如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

在这种情况下,您可能最好完全转换字段值。这可以在迁移中完成,一旦完成,您就不必担心MySQL如何存储数据。这也是实现数据库独立性的一步。

因此,迁移基本上会做三件事:

  • 添加标记列以跟踪已转换的记录
  • 迭代每条记录,转换加密值并设置标志
  • 在处理完所有记录后删除标志列

迁移可能如下所示:

class ConvertMySqlEncryptedData < ActiveRecord::Migration
  # Local proxy class to prevent interaction issues with the real User class
  class User < ActiveRecord::Base
  end

  def up
    # Check to see if the flag has already been created (indicates that migration may have failed midway through)
    unless column_exists?(:users, :encrypted_field_converted)
      # Add the flag field to the table
      change_table :users do |t|
        t.boolean :encrypted_field_converted, null: false, default: false
      end
    end

    # Add an index to make the update step go much more quickly
    add_index :users, :encrypted_field_converted, unique: false

    # Make sure that ActiveRecord can see the new column
    User.reset_column_information

    # Setup for AES 256 bit cipher-block chaining symetric encryption
    alg = "AES-256-CBC"
    digest = Digest::SHA256.new
    digest.update("symetric key")
    key = digest.digest
    iv = OpenSSL::Cipher::Cipher.new(alg).random_iv
    key64 = Base64.encode(key)

    # Don't update timestamps
    ActiveRecord::Base.record_timestamps = false
    begin
      # Cycle through the users that haven't yet been updated
      User.where(encrypted_field_converted: false).pluck("CAST(AES_DECRYPT(Pass, 'kkk') AS CHAR(50)) Pass").each do |user|
        # Re-encode the password with OpenSSL AES, based on the setup above
        new_pass = aes.update(user.pass).final

        # Update the password on the row, and set the flag to indicate that conversion has occurred
        user.update_attributes(pass: new_pass, encrypted_field_converted: true)
      end
    ensure
      # Reset timestamp recording
      ActiveRecord::Base.record_timestamps = true
    end
  end

  def down
    # To undo or not undo, that is the question...
  end
end

这不是我的首要问题,因此加密可能存在问题。在结构方面,它应该是良好的形状,并考虑到许多事情:

  • 使用标志指示进度
  • 提供增量数据库处理
  • 使用标志字段上的索引来提高查询性能,尤其是在需要多次运行才能完成处理时
  • 避免更新updated_at列,以防止覆盖可能有用的先前值(这不是重大更改,因此updated_at不需要更新)
  • 仅选取pass字段,以便最大限度地减少转移开销

现在,您可以根据应用程序的需要查询pass并加密/解密。您可以在应用程序级别记录和支持该字段,而不是依赖于数据库实现。

我花了几年时间咨询和进行数据库转换,从一个数据库产品到另一个数据库产品,或作为重要版本升级的一部分。当需要升级时,它还允许开发使用更轻量级的数据库(例如SQLite)或测试其他产品的可行性。避免使用MySQL加密等特定于数据库的功能,从长远来看,可以为您(或您的雇主)节省大量资金和麻烦。数据库独立是你的朋友;拥抱它并使用ActiveRecord为您提供的内容。