v7.0.0
更多資訊請前往 rubyonrails.org: 更多在 Ruby on Rails

Active Record 加密(Encryption)

本指南涵蓋使用 Active Record 加密您的資料庫資訊。

閱讀本指南後,您將瞭解:

Active Record 支援應用級加密。它透過宣告應該加密哪些屬性並在必要時無縫加密和解密它們來工作。加密層位於資料庫和應用程式之間。應用程式將訪問未加密的資料,但資料庫將對其進行加密儲存。

1 基本用法

1.1 設定

首先,您需要向 rails 憑據 新增一些 keys。執行 bin/rails db:encryption:init 生成隨機金鑰集:

$ bin/rails db:encryption:init
Add this entry to the credentials of the target environment:

active_record_encryption:
  primary_key: EGY8WhulUOXixybod7ZWwMIL68R9o5kC
  deterministic_key: aPA5XyALhf75NNnMzaspW7akTfZp0lPY
  key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz

這些生成的 keys 和 salt 的長度為 32 位元組。如果你自己生成這些,你應該使用的最小長度是 12 位元組的主鍵(這將用於派生 AES 32 位元組的金鑰)和 20 位元組的鹽。

1.2 加密屬性宣告

可加密屬性在 model 級別定義。這些是由同名列支援的正常 Active Record 屬性。

class Article < ApplicationRecord
  encrypts :title
end

庫將在將這些屬性儲存到資料庫之前透明地加密它們,並在檢索它們的 values 時將它們解密:

article = Article.create title: "Encrypt it all!"
article.title # => "Encrypt it all!"

但是,在幕後,執行的 SQL 看起來像這樣:

INSERT INTO `articles` (`title`) VALUES ('{\"p\":\"n7J0/ol+a7DRMeaE\",\"h\":{\"iv\":\"DXZMDWUKfp3bg/Yu\",\"at\":\"X1/YjMHbHD4talgF9dt61A==\"}}')

加密會佔用列中的額外空間。當使用內建信封加密 key 提供程式時,您可以估計最壞情況的過載大約為 250 位元組。對於中型和大型文字列,此過載可以忽略不計,但對於 255 位元組的 string 列,您應該相應地增加它們的限制(推薦 510)。

額外空間的原因是 Base 64 編碼和額外的元資料與加密的 values 一起儲存。

1.3 確定性和非確定性加密

預設情況下,Active Record 加密使用非確定性方法進行加密。這意味著用相同的密碼對相同的內容加密兩次會導致不同的密文。這有利於安全,因為它使加密內容的加密分析變得更加困難,但它使查詢資料庫變得不可能。

您可以使用 deterministic: 選項以確定性方式生成初始化向量,從而有效地查詢加密資料。

class Author < ApplicationRecord
  encrypts :email, deterministic: true
end

Author.find_by_email("some@email.com") # You can query the model normally

除非您需要查詢資料,否則建議使用預設值(非確定性)。

在非確定性模式下,它使用帶有 256 位 key 和隨機初始化向量的 AES-GCM。在確定性模式下,它也使用 AES-GCM,但初始化向量生成為 key 的 HMAC-SHA-256 摘要和要加密的內容。

您可以透過不配置 deterministic_key 來禁用確定性加密。

2 特點

2.1 Action Text

您可以透過在宣告中傳遞 encrypted: true 來加密 action text 屬性。

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

尚不支援將單個加密選項傳遞給 action text 屬性。它將使用配置了全域性加密選項的非確定性加密。

2.2 燈具

您可以透過將此選項新增到 test.rb 來自動加密 Rails 裝置:

config.active_record.encryption.encrypt_fixtures = true

啟用後,所有可加密屬性將根據 model 中定義的加密設定進行加密。

2.2.1 Action Text 燈具

要加密 action text 固定裝置,您應該將它們放在 fixtures/action_text/encrypted_rich_texts.yml 中。

2.3 支援的型別

active_record.encryption 將在加密之前使用底層型別序列化 values,但它們必須可序列化為字串。開箱即用支援結構型別,如 serialized

如果需要支援自定義型別,推薦的方式是使用序列化屬性。序列化屬性的宣告應該在加密宣告之前**:

 好的
class Article < ApplicationRecord
  serialize :title, Title
  encrypts :title
end

 錯誤的
class Article < ApplicationRecord
  encrypts :title
  serialize :title, Title
end

2.4 忽略大小寫

在查詢確定性加密的資料時,您可能需要忽略大小寫。這裡有兩個選項可以幫助您。

您可以在宣告加密屬性時使用 :downcase 選項以在加密發生之前將內容小寫。

class Person
  encrypts :email_address, deterministic: true, downcase: true
end

使用:downcase時,原來的case丟失。在某些情況下,您可能只想在查詢時忽略大小寫,同時儲存原始大小寫。對於這些情況,您可以使用選項 :ignore_case。這需要您新增一個名為 original_<column_name> 的新列來儲存大小寫不變的內容:

class Label
  encrypts :name, deterministic: true, ignore_case: true # the content with the original case will be stored in the column `original_name`
end

2.5 支援未加密資料

為了簡化未加密資料的 migrations,該庫包含選項 config.active_record.encryption.support_unencrypted_data。設定為 true 時:

  • 嘗試讀取未加密的加密屬性將正常工作,不會引發任何錯誤
  • 具有確定性加密屬性的查詢將包括它們的“明文”版本,以支援查詢加密和未加密的內容。您需要設定 config.active_record.encryption.extend_queries = true 以啟用此功能。

此選項適用於過渡期,而明文資料和加密資料需要共存。它們的 value 預設為 false,這是任何應用程式的推薦目標:處理未加密資料時會引發錯誤。

2.6 支援以前的加密方案

更改屬性的加密屬性可能會破壞現有資料。例如,假設您想將“確定性”屬性設定為“非確定性”。如果只是更改了 model 中的宣告,則讀取現有密文將失敗,因為它們現在不同了。

為了支援這些情況,您可以宣告將在兩種情況下使用的先前加密方案:

  • 在讀取加密資料時,如果當前方案不起作用,Active Record 加密將嘗試以前的加密方案。
  • 查詢確定性資料時,將使用先前方案的密文新增到查詢中,以便查詢與使用不同方案加密的資料無縫協作。您需要設定 config.active_record.encryption.extend_queries = true 以啟用此功能。

您可以配置以前的加密方案:

  • 全球
  • 基於每個屬性
2.6.1 全球以前的加密方案

您可以透過使用 application.rb 中的 previous 配置屬性將它們新增為屬性列表來新增以前的加密方案:

config.active_record.encryption.previous = [ { key_provider: MyOldKeyProvider.new } ]
2.6.2 每屬性加密方案

宣告屬性時使用 :previous

class Article
  encrypts :title, deterministic: true, previous: { deterministic: false }
end
2.6.3 加密方案和確定性屬性

新增以前的加密方案時:

  • 使用非確定性加密,新資訊將始終使用最新(當前)加密方案進行加密。
  • 使用確定性加密,預設情況下,新資訊將始終使用最舊的加密方案進行加密。

原因是,對於確定性加密,您通常希望密文保持不變。您可以透過設定 deterministic: { fixed: false } 來更改此行為。在這種情況下,它將使用最新 加密方案來加密新資料。

2.7 唯一約束

唯一約束只能用於確定性加密的資料。

2.7.1 唯一驗證

只要啟用了擴充套件查詢 (config.active_record.encryption.extend_queries = true),通常就支援唯一驗證。

class Person
  validates :email_address, uniqueness: true
  encrypts :email_address, deterministic: true, downcase: true
end

在組合加密和未加密資料以及配置以前的加密方案時,它們也將起作用。

如果您想忽略大小寫,請確保在 encrypts 宣告中使用 downcase:ignore_case:。在驗證中使用 case_sensitive: 選項將不起作用。

2.7.2 唯一索引

為了支援確定性加密列的唯一索引,您需要確保它們的密文永遠不會改變。

為了鼓勵這一點,預設情況下,當配置多個加密方案時,確定性屬性將始終使用最舊的加密方案。除此之外,您需要確保這些屬性的加密屬性不會更改,否則唯一索引將不起作用。

class Person
  encrypts :email_address, deterministic: true
end

2.8 過濾引數命名為加密列

預設情況下,加密列被配置為在 Rails 日誌中自動過濾。您可以透過將其新增到 application.rb 來禁用此行為:

config.active_record.encryption.add_to_filter_parameters = false

如果您想從此自動過濾中排除特定列,請將它們新增到 config.active_record.encryption.excluded_from_filter_parameters

2.9 編碼

該庫將保留非確定性加密的字串 values 的編碼。

對於 values 確定性加密,預設情況下,庫將強制使用 UTF-8 編碼。原因是編碼與加密的有效負載一起儲存。這意味著相同的值具有不同的編碼會在加密時產生不同的密文。您通常希望避免這種情況以保持查詢和唯一性約束正常工作,因此庫將代表您自動執行轉換。

您可以使用以下命令為確定性加密配置所需的預設編碼:

config.active_record.encryption.forced_encoding_for_deterministic_encryption = Encoding::US_ASCII

您可以禁用此行為並在所有情況下保留編碼:

config.active_record.encryption.forced_encoding_for_deterministic_encryption = nil

3 Key 管理

Key 管理策略由關鍵提供商實施。您可以全域性或按屬性配置金鑰提供程式。

3.1 內建 Key 提供程式

3.1.1 DerivedSecretKeyProvider

將使用 PBKDF2 從提供的密碼派生的 keys 的金鑰提供程式。

config.active_record.encryption.key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(["some passwords", "to derive keys from. ", "These should be in", "credentials"])

預設情況下,active_record.encryption 使用 active_record.encryption.primary_key 中定義的 keys 配置 DerivedSecretKeyProvider

3.1.2 信封加密KeyProvider

實現一個簡單的信封加密 策略:

  • 它為每個資料加密操作生成一個隨機的 key
  • 它將資料-key 與資料本身一起儲存,並使用憑證 active_record.encryption.primary_key 中定義的主要 key 進行加密。

您可以透過將其新增到 application.rb 來進行配置:

config.active_record.encryption.key_provider = ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new

與其他內建金鑰提供程式一樣,您可以在 active_record.encryption.primary_key 中提供主要 keys 列表,以實現金鑰輪換方案。

3.2 自定義 Key 提供程式

對於更高階的 key 管理方案,您可以在初始化程式中配置自定義 key 提供程式:

ActiveRecord::Encryption.key_provider = MyKeyProvider.new

key 提供者必須實現這個介面:

class MyKeyProvider
  def encryption_key
  end

  def decryption_keys(encrypted_message)
  end
end

兩種方法都返回 ActiveRecord::Encryption::Key 物件:

  • encryption_key 返回用於加密某些內容的 key
  • decryption keys 返回用於解密給定訊息的潛在 keys 列表

key 可以包含將與訊息一起未加密儲存的任意標籤。您可以在解密時使用 ActiveRecord::Encryption::Message#headers 來檢查那些 values。

3.3 Model 特定的 Key 提供者

您可以使用 :key_provider 選項在每個類的基礎上配置 key 提供程式:

class Article < ApplicationRecord
  encrypts :summary, key_provider: ArticleKeyProvider.new
end

3.4 Model 特定的 Keys

您可以使用 :key 選項在每個類的基礎上配置給定的 key:

class Article < ApplicationRecord
  encrypts :summary, key: "some secret key for article summaries"
end

key 將在內部用於派生用於加密和解密資料的 key。

3.5 旋轉 Keys

active_record.encryption 可以使用 keys 列表來支援實現金鑰輪換方案:

  • last key 將用於加密新內容。
  • 解密內容時將嘗試所有 keys,直到成功為止。
active_record
  encryption:
    primary_key:
        - a1cc4d7b9f420e40a337b9e68c5ecec6 # Previous keys can still decrypt existing content
        - bc17e7b413fd4720716a7633027f8cc4 # Active, encrypts new content
    key_derivation_salt: a3226b97b3b2f8372d1fc6d497a0c0d3

這透過新增新的 keys、重新加密內容和刪除舊的 keys 來啟用您保留 keys 的簡短列表的工作流。

確定性加密目前不支援旋轉 keys。

Active Record 加密還沒有提供對 key 輪換過程的自動管理。所有的部分都在那裡,但這還沒有實現。

3.6 儲存 Key 引用

有一個設定 active_record.encryption.store_key_references 可以用來使 active_record.encryption 在加密訊息本身中儲存對加密 key 的引用。

config.active_record.encryption.store_key_references = true

這使得解密效能更高,因為系統現在可以直接定位 keys,而不是嘗試 keys 列表。付出的代價是儲存:加密資料的大小會更大一些。

4 介面

4.1 基礎介面

ActiveRecord 加密旨在以宣告方式使用,但它提供了用於高階使用場景的 API。

4.1.1 加解密
article.encrypt # encrypt or re-encrypt all the encryptable attributes
article.decrypt # decrypt all the encryptable attributes
4.1.2 讀取密文
article.ciphertext_for(:title)
4.1.3 檢查屬性是否加密
article.encrypted_attribute?(:title)

5 配置

5.1 配置選項

您可以透過在您的 application.rb(最常見的場景)或特定環境配置檔案 config/environments/<env name>.rb 中設定它們來配置 Active Record 加密選項,如果您想在每個環境的基礎上設定它們。

所有配置選項都在 active_record.encryption.config 中命名。例如:

config.active_record.encryption.key_provider = ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new
config.active_record.encryption.store_key_references = true
config.active_record.encryption.extend_queries = true

可用的配置選項是:

Key Value
support_unencrypted_data 當為 true 時,可以正常讀取未加密的資料。當為假時,它會升高。預設值:假。
extend_queries 當為 true 時,如果需要,將修改引用確定性加密屬性的查詢以包含額外的 values。這些額外的 values 將是該值的乾淨版本,當 support_unencrypted_data 為真時)和 values 使用以前的加密方案加密(如果有的話)(與 previous: 選項一起提供)。預設值:false(實驗性)。
encrypt_fixtures 當為 true 時,裝置中的可加密屬性將在載入時自動加密。預設值:假。
store_key_references 當為真時,對加密金鑰的引用儲存在加密訊息的標題中。當使用多個 keys 時,這可以加快解密速度。預設值:假。
add_to_filter_parameters 當為 true 時,加密的屬性名稱會自動新增到 過濾引數列表 中,不會在日誌中顯示。預設值:真。
excluded_from_filter_parameters 您可以配置一個引數列表,當 add_to_filter_parameters 為 true 時,這些引數不會被過濾掉。預設: []。
validate_column_size 新增基於列大小的驗證。建議使用高度可壓縮的有效載荷來防止儲存巨大的 values。預設值:真。
primary_key 用於派生根資料加密 keys 的 keys 的金鑰或列表。它們的使用方式取決於配置的金鑰提供程式。最好透過憑證 active_record_encryption.primary_key 對其進行配置。
deterministic_key 用於確定性加密的 keys 的金鑰或列表。最好透過憑證 active_record_encryption.deterministic_key 對其進行配置。
key_derivation_salt 派生 keys 時使用的鹽。最好透過憑證 active_record_encryption.key_derivation_salt 對其進行配置。
forced_encoding_for_deterministic_encryption 確定性加密的屬性的預設編碼。您可以透過將此選項設定為 nil 來禁用強制編碼。預設為 Encoding::UTF_8

建議使用 Rails 內建憑證支援來儲存 keys。如果您更喜歡透過配置屬性手動設定它們,請確保不要將它們與您的程式碼一起提交(例如:使用環境變數)。

5.2 加密上下文

加密上下文定義了在給定時刻使用的加密元件。有一個基於您的全域性配置的預設加密上下文,但您可以為給定屬性或在執行特定程式碼塊時配置自定義上下文。

加密上下文是一種靈活但高階的配置機制。大多數使用者不應該關心它們。

加密上下文的主要組成部分是:

  • encryptor:公開用於加密和解密資料的內部API。它與 key_provider 互動以構建加密訊息並處理它們的序列化。加密/解密本身由 cipher 完成,序列化由 message_serializer 完成。
  • cipher 加密演算法本身(Aes 256 GCM)
  • key_provider 服務於加密和解密 keys。
  • message_serializer:序列化和反序列化加密的有效負載(Message)。

如果您決定構建自己的 message_serializer,請務必使用不能反序列化任意物件的安全機制。一個常見的支援方案是加密現有的未加密資料。攻擊者可以利用它在加密之前輸入篡改的有效載荷並執行 RCE 攻擊。這意味著自定義序列化程式應避免使用 MarshalYAML.load(使用 YAML.safe_load 代替)或 JSON.load(使用 JSON.parse 代替)。

5.2.1 全域性加密上下文

全域性加密上下文是預設使用的上下文,並在 application.rb 或環境配置檔案中配置為其他配置屬性。

config.active_record.encryption.key_provider = ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new
config.active_record_encryption.encryptor = MyEncryptor.new
5.2.2 每屬性加密上下文

您可以透過在屬性宣告中傳遞加密上下文引數來覆蓋它們:

class Attribute
  encrypts :title, encryptor: MyAttributeEncryptor.new
end
5.2.3 執行程式碼塊時的加密上下文

您可以使用 ActiveRecord::Encryption.with_encryption_context 為給定的程式碼塊設定加密上下文:

ActiveRecord::Encryption.with_encryption_context(encryptor: ActiveRecord::Encryption::NullEncryptor.new) do
  ...
end
5.2.4 內建加密上下文
5.2.4.1 禁用加密

您可以在不加密的情況下執行程式碼:

ActiveRecord::Encryption.without_encryption do
   ...
end

這意味著讀取加密文字將返回密文,儲存的內容將不加密儲存。

5.2.4.2 保護加密資料

您可以在不加密的情況下執行程式碼但防止覆蓋加密的內容:

ActiveRecord::Encryption.protecting_encrypted_data do
   ...
end

如果您想保護加密資料,同時仍然允許有人針對它執行任意程式碼(例如:在 Rails 控制檯中),這會很方便。

回饋

我們鼓勵您幫助提高本指南的品質。

如果您發現任何拼寫錯誤或資訊錯誤,請提供回饋。 要開始回饋,您可以閱讀我們的 回饋 部分。

您還可能會發現不完整的內容或不是最新的內容。 請務必為 main 新增任何遺漏的文件。假設是 非穩定版指南(edge guides) 請先驗證問題是否已經在主分支上解決。 請前往 Ruby on Rails 指南寫作準則 查看寫作風格和慣例。

如果由於某種原因您發現要修復的內容但無法自行修補,請您 提出 issue

關於 Ruby on Rails 的任何類型的討論歡迎提供任何文件至 rubyonrails-docs 討論區