Это руководство раскрывает шифрование информации вашей базы данных с помощью Active Record.
После его прочтения, вы узнаете:
Active Record предоставляет шифрование на уровне приложения. Он работает, объявляя какие атрибуты должны быть зашифрованы, и бесшовно шифрует и дешифрует их по необходимости. Уровень шифрования находится между базой данных и приложением. У приложения будет доступ к незашифрованным данным, но база данных будет хранить их зашифрованными.
Шифрование Active Record существует для защиты чувствительной информации в вашем приложении. Типичным примером является личная информация от пользователей. Но зачем нам шифрование не уровне приложения, если уже есть шифрование самой базы данных?
В качестве немедленного практического бонуса, шифрование чувствительных атрибутов добавляет дополнительный слой безопасности. Например, если злоумышленник получил доступ к вашей базе данных, ее резервной копии или логам приложения, он не сможет понять зашифрованную информацию. Дополнительно шифрование может предотвратить от непредумышленного раскрытия чувствительных данных разработчиками в логах приложения.
Но что более важно, используя шифрование Active Record, вы определяете, что составляет чувствительную информацию в вашем приложение на уровне кода. Шифрование Active Record включает детальный контроль доступа к данным в вашем приложении и сервисах, использующих данные из вашего приложения. Например, рассмотрите аудируемые консоли Rails, защищающие зашифрованные данные, или проверки встроенной системы автоматической фильтрации параметров контроллера.
Сначала необходимо добавить несколько ключей в ваши учетные данные Rails. Запустите 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
Эти сгенерированные значения длиной 32 байта. Если вы генерируете их самостоятельно, минимальная длина, которую следует использовать, это 12 байт для первичного ключа (он будет использоваться для получения 32-байтного ключа AES) и 20 байт для соли.
Шифруемые атрибуты определяются на уровне модели. Это обычные атрибуты Active Record на основе столбца с тем же именем.
class Article < ApplicationRecord
encrypts :title
end
Библиотека прозрачно зашифрует эти атрибуты перед сохранением в базу данных и дешифрует при извлечении:
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==\"}}')
Шифрование требует дополнительное пространство из-за кодировки Base64 и хранимых метаданных вместе с зашифрованной нагрузкой. При использовании встроенного конвертного провайдера ключа шифрования можно оценить перерасход в примерно 255 байтов. Этот перерасход несущественен при большом размере. Не только потому, что он растворяется в объеме, а потому, что библиотека по умолчанию использует сжатие, что может предложить до 30% экономии объема над нешифрованной версией большой нагрузки.
Важно знать о размерах строкового столбца: в современных базах данных размер столбца определяет количество символов, которые можно разместить, а не количество байтов. Например, в UTF-8 каждый символ может занимать до четырех байтов, поэтому, потенциально, столбец базы данных, использующей UTF-8, может хранить до четырех своих размеров в байтах. Теперь шифрованная нагрузка это бинарные строки, сериализованные как Base64, поэтому они могут храниться в обычных столбцах string
. Так как они являются последовательностью байтов ASCII, шифрованный столбец может занимать до четырех размеров его чистой версии. Таким образом, даже если байты, хранимые в базе данных, такие же, столбец должен быть в четыре раза больше.
На практике это означает:
Несколько примеров:
Содержимое для шифрования | Изначальный размер столбца | Рекомендованный размер шифрованного столбца | Перерасход (в худшем случае) |
---|---|---|---|
Адреса email | string(255) | string(510) | 255 bytes |
Короткая последовательность emoji | string(255) | string(1020) | 255 bytes |
Краткий текст, написанный на не западных алфавитах | string(500) | string(2000) | 255 bytes |
Произвольный длинный текст | text | text | незначительный |
По умолчанию Active Record Encryption использует недетерминированный подход к шифрованию. Недетерминированный в данном контексте означает, что шифрование одного и того же содержимого с тем же самым паролем дважды приведет к различным зашифрованным текстам. Данный подход улучшает безопасность, делая криптоанализ зашифрованного содержимого сложнее, но невозможным запросы к базе данных.
Можно использовать опцию deterministic:
для генерации векторов инициализации детерминированным образом, эффективно включая запросы по зашифрованным данным.
class Author < ApplicationRecord
encrypts :email, deterministic: true
end
Author.find_by_email("some@email.com") # Можно делать запросы, как обычно
Недетерминированный подход рекомендован, если вам не нужно делать запросы по данным.
В недетерминированном режиме Active Record использует AES-GCM с 256-битным ключом и случайным вектором инициализации. В детерминированном режиме также используется AES-GCM, но вектор инициализации генерируется как дайджест HMAC-SHA-256 ключа и шифруемого содержимого.
Можно отключить детерминированное шифрования опуская deterministic_key
.
Можно шифровать атрибуты action text, передав encrypted: true
в их объявлении.
class Message < ApplicationRecord
has_rich_text :content, encrypted: true
end
Передача отдельных опций шифрования в атрибуты action text пока еще не поддерживается. Он будет использовать недетерминированное шифрование с помощью глобально настроенных опций шифрования.
Можно сделать фикстуры Rails шифруемыми автоматически, добавив эту опцию в ваш test.rb
:
config.active_record.encryption.encrypt_fixtures = true
Когда включена, все шифруемые атрибуты будут зашифрованы в соответствии с настройками шифрования, определенными в модели.
Чтобы зашифровать фикстуры action text, следует поместить их в fixtures/action_text/encrypted_rich_texts.yml
.
active_record.encryption
будет сериализовывать значения с помощью лежащего в основе типа до их шифрования, но они должны быть сериализуемыми как строки. Структурируемые типы, наподобие serialized
, поддерживаются из коробки.
Если нужна поддержка пользовательского типа, рекомендованным способом является использование сериализуемого атрибута. Объявление сериализуемого атрибута должно идти перед объявлением шифрования:
# ПРАВИЛЬНО
class Article < ApplicationRecord
serialize :title, Title
encrypts :title
end
# НЕПРАВИЛЬНО
class Article < ApplicationRecord
encrypts :title
serialize :title, Title
end
Возможно, вам необходимо игнорировать регистр при запросе детерминировано зашифрованных данных. Два подхода делают это проще:
Можно использовать опцию :downcase
при объявлении шифруемого атрибута, чтобы сделать его содержимое в нижнем регистре до шифрования.
class Person
encrypts :email_address, deterministic: true, downcase: true
end
При использовании :downcase
оригинальный регистр теряется. В некоторых ситуациях хочется игнорировать регистр только при запросе, но также хранить оригинальный регистр. Для таких ситуаций можно использовать опцию :ignore_case
. Это требует добавления нового столбца с именем original_<column_name>
, для хранения содержимого с неизмененным регистром:
class Label
encrypts :name, deterministic: true, ignore_case: true # содержимое с оригинальным регистром будет храниться в столбце `original_name`
end
Чтобы облегчить миграции нешифрованных данных, библиотека включает опцию config.active_record.encryption.support_unencrypted_data
. Когда установлена true
:
config.active_record.encryption.extend_queries = true
чтобы включить это.
Эти опции предназначены для переходного периода, пока исходные и шифрованные данные вынуждены сосуществовать. Обе установлены как false
по умолчанию, что является рекомендованной целью для любого приложения: будут вызваны ошибки при работе с незашифрованными данными.
Изменение свойств шифрования может поломать существующие данные. Например, представьте, что вы хотите сделать детерминированный атрибут недетерминированным. Если вы просто измените объявление в модели, чтение существующего шифра провалится, так как метод шифрования теперь другой.
Чтобы поддержать такие ситуации, можно объявить предыдущие схемы шифрования, которые будут использованы в двух сценариях:
config.active_record.encryption.extend_queries = true
.
Можно настроить предыдущие схемы шифрования:
Можно добавить предыдущие схемы шифрования, добавив их как список свойств с помощью конфигурационной настройки previous
в application.rb
:
config.active_record.encryption.previous = [ { key_provider: MyOldKeyProvider.new } ]
Используйте :previous
при объявлении атрибута:
class Article
encrypts :title, deterministic: true, previous: { deterministic: false }
end
При добавлении предыдущих схем шифрования:
Обычным при недетерминированном шифровании является то, что мы хотим, чтобы шифры оставались постоянными. Это поведение можно изменить, установив deterministic: { fixed: false }
. В этом случае она будет использовать новейшую схему шифрования для шифрования новых данных.
Ограничения уникальности могут быть использованы только с детерминировано зашифрованными данными.
Валидации уникальности работают нормально, когда включены расширенные запросы (config.active_record.encryption.extend_queries = true
).
class Person
validates :email_address, uniqueness: true
encrypts :email_address, deterministic: true, downcase: true
end
Они также будут работать при комбинации шифрованных и нешифрованных данных и когда настроены предыдущие схемы шифрования.
Если хотите игнорировать регистр, убедитесь что используются downcase:
или ignore_case:
в объявлении encrypts
. Использование опции case_sensitive:
в валидации не будет работать.
Чтобы поддерживать индексы уникальности на детерминировано шифруемом столбце, нужно обеспечить, чтобы их шифр никогда не менялся.
Для этого детерминированные атрибуты будут всегда использовать старейшую схему шифрования по умолчанию, когда настроено несколько схем шифрования. В противном случае вам следует обеспечить, чтобы эти свойства шифрования для этих атрибутов не менялись, иначе индексы уникальности не будут работать.
class Person
encrypts :email_address, deterministic: true
end
По умолчанию шифруемые столбцы настраиваются как автоматически фильтруемые в логах Rails. Это поведение можно отключить, добавив следующее в application.rb
:
При генерации фильтруемого параметра, это будет использовать имя модели как префикс. Например, для Person#name
фильтруемым параметром будет person.name
.
config.active_record.encryption.add_to_filter_parameters = false
В случае, если хотите исключить отдельные столбцы из этой автоматической фильтрации, добавьте их в config.active_record.encryption.excluded_from_filter_parameters
.
Библиотека сохраняет кодировку для строковых значений, шифруемых недетерминировано.
Так как кодировка хранится вместе с зашифрованным содержимым, детерминировано зашифрованные значения по умолчанию будут приведены к кодировке UTF-8. Следовательно, то же самое значение в разных кодировках приведет к разным шифровкам при шифровании. Обычно этого хочется избежать, чтобы работал поиск и ограничения уникальности, поэтому библиотека выполнит преобразование сама автоматически.
Можно сконфигурировать желаемую кодировку по умолчанию для детерминированного шифрования с помощью:
config.active_record.encryption.forced_encoding_for_deterministic_encryption = Encoding::US_ASCII
А также можно отключить это поведение и сохранить кодировку во всех случаях с помощью:
config.active_record.encryption.forced_encoding_for_deterministic_encryption = nil
Провайдеры ключа реализуют стратегии управления ключами. Можно настроить провайдеры ключа глобально или на основе атрибута.
Провайдер ключа, который отдает ключи, производные от предоставленных паролей, с помощью PBKDF2.
config.active_record.encryption.key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(["some passwords", "to derive keys from. ", "These should be in", "credentials"])
По умолчанию active_record.encryption
настраивает DerivedSecretKeyProvider
с помощью ключей, определенных в active_record.encryption.primary_key
.
Реализует простую стратегию конвертного шифрования:
active_record.encryption.primary_key
.
Можно настроить Active Record использовать этот провайдер ключа, добавив следующее в ваш application.rb
:
config.active_record.encryption.key_provider = ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new
Как и для других встроенных провайдеров ключей, можно предоставить список первичных ключей в active_record.encryption.primary_key
для реализации схем ротации ключа.
Для расширенных схем управления ключами можно настроить пользовательский провайдер ключа в инициализаторе:
ActiveRecord::Encryption.key_provider = MyKeyProvider.new
Провайдер ключа должен реализовывать этот интерфейс:
class MyKeyProvider
def encryption_key
end
def decryption_keys(encrypted_message)
end
end
Оба метода должны возвращать объекты ActiveRecord::Encryption::Key
:
encryption_key
возвращает ключ для шифрования некоторого содержимого
decryption keys
возвращает список потенциальных ключей для дешифрования заданного сообщения
Ключ может включать произвольные теги, которые будет храниться нешифрованными с сообщением. Для просмотра этих значений можно использовать ActiveRecord::Encryption::Message#headers
при дешифровке.
Можно настроить провайдер ключа на основе класса с помощью опции :key_provider
:
class Article < ApplicationRecord
encrypts :summary, key_provider: ArticleKeyProvider.new
end
Можно настроить заданный ключ на основе класса с помощью опции :key
:
class Article < ApplicationRecord
encrypts :summary, key: "some secret key for article summaries"
end
Active Record использует этот ключ для создания ключа, используемого для шифрования и дешифрования данных.
active_record.encryption
может работать со списками ключей для реализации схем ротации ключей:
active_record_encryption:
primary_key:
- a1cc4d7b9f420e40a337b9e68c5ecec6 # Предыдущие ключи все еще могут дешифровывать содержимое
- bc17e7b413fd4720716a7633027f8cc4 # Активный, шифрует новое содержимое
key_derivation_salt: a3226b97b3b2f8372d1fc6d497a0c0d3
Это позволяет такой цикл, в котором хранится короткий список ключей, и при добавлении нового ключа содержимое перешифровывается, и старые ключи удаляются.
Сейчас ротация ключей не поддерживается для детерминированного шифрования.
Active Record Encryption пока что не предоставляет автоматического управления над ротацией ключей. Все для этого уже есть, но пока не реализовано.
Можно сконфигурировать active_record.encryption.store_key_references
, чтобы active_record.encryption
хранила ссылку на ключ шифрования в самом зашифрованном сообщении.
config.active_record.encryption.store_key_references = true
Это обеспечивает более производительное дешифрование, так как система может найти нужные ключи вместо перебора списка ключей. Цена, которую нужно заплатить, это место: шифрованные данные будут немного больше в размере.
Шифрование ActiveRecord подразумевает декларативное использование, но оно также предлагает API для сценариев продвинутого использования.
article.encrypt # шифрует или перешифровывает все шифруемые атрибуты
article.decrypt # дешифрует все шифруемые атрибуты
article.ciphertext_for(:title)
article.encrypted_attribute?(:title)
Можно настроить опции Active Record Encryption в вашем application.rb
(самый распространенный сценарий) или в файле определенной среды config/environments/<env name>.rb
, если хотите установить их на основе среды.
Для хранения ключей рекомендуется использовать встроенную в Rails поддержку учетных данных. Если предпочитаете установить их вручную с помощью конфигурационных свойств, убедитесь, что не храните их вместе с кодом (например, используете переменные среды).
config.active_record.encryption.support_unencrypted_data
Когда true, нешифрованные данные читаются, как обычно. Когда false, будут вызываться ошибки. По умолчанию: false
.
config.active_record.encryption.extend_queries
Когда true, запросы, ссылающиеся на детерминировано шифруемые атрибуты, будут модифицированы, чтобы по необходимости включать дополнительные значения. Эти дополнительные значения будут включать чистую версию значения (когда config.active_record.encryption.support_unencrypted_data
true) и значения, зашифрованные с помощью предыдущих схем шифрования, если они имеются (предоставлены с помощью опции previous:
). По умолчанию: false
(экспериментально).
config.active_record.encryption.encrypt_fixtures
Когда true, шифруемые атрибуты в фикстурах будут автоматически зашифрованы при загрузке. По умолчанию: false
.
config.active_record.encryption.store_key_references
Когда true, ссылка на ключ шифрования сохраняется в заголовках зашифрованного сообщения. Это сделано для более быстрого дешифрования при использовании нескольких ключей. По умолчанию: false
.
config.active_record.encryption.add_to_filter_parameters
Когда true, имена шифруемых атрибутов автоматически добавляются в config.filter_parameters
, которые не показываются в логах. По умолчанию: true
.
config.active_record.encryption.excluded_from_filter_parameters
Можно настроить список параметров, которые не будут отфильтрованы, когда config.active_record.encryption.add_to_filter_parameters
true. По умолчанию: []
.
config.active_record.encryption.validate_column_size
Добавляет валидацию, основанную на размере столбца. Это рекомендовано, чтобы избежать хранения огромных значений с использованием очень сжатой полезной нагрузки. По умолчанию: true
.
config.active_record.encryption.primary_key
Ключ или список ключей, используемых для воспроизведения корневых ключей шифрования данных. Способ, которым они используются, зависит от настроенного провайдера ключа. Предпочтительнее настроить их с помощью учетных данных active_record_encryption.primary_key
.
config.active_record.encryption.deterministic_key
Ключ или список ключей, используемых для детерминированного шифрования. Предпочтительнее настроить их с помощью учетных данных active_record_encryption.deterministic_key
.
config.active_record.encryption.key_derivation_salt
Соль, используемая при воспроизведении ключей. Предпочтительнее настроить ее с помощью учетных данных active_record_encryption.key_derivation_salt
.
config.active_record.encryption.forced_encoding_for_deterministic_encryption
Кодировка по умолчанию для детерминированно шифруемых атрибутов. Можно отключить принудительную кодировку, установив этой опции nil
. По умолчанию Encoding::UTF_8
.
Контекст шифрования определяет компоненты шифрования, используемые в данный момент. Есть контекст шифрования по умолчанию, основанный на глобальной конфигурации, но можно настроить произвольный контекст для заданного атрибута или при запуске определенного блока кода.
Контексты шифрования это гибкий, но сложный конфигурационных механизм. Большинству пользователей можно не беспокоиться о них.
Основные компоненты контекстов шифрования следующие:
encryptor
: представляет внутренний API для шифрования и дешифрования данных. Он взаимодействует с key_provider
для создания зашифрованных сообщений и имеет дело с их сериализацией. Само шифрование/дешифрование выполняется cipher
, а сериализация message_serializer
.
cipher
: сам алгоритм шифрования (Aes 256 GCM)
key_provider
: отдает ключи шифрования и дешифрования.
message_serializer
: сериализует и десериализует зашифрованную нагрузку (Message
).
Если решили создать свой message_serializer
, важно использовать безопасные механизмы, которые не могут десериализовывать произвольные объекты. Обычный поддерживаемый сценарий, когда шифруются существующие незашифрованные данные. Злоумышленник может использовать это, вводя подделанную нагрузку до шифрования, и выполняя атаки RCE. Это означает, что пользовательские сериализаторы должны избегать Marshal
, YAML.load
(вместо него используйте YAML.safe_load
) или JSON.load
(вместо него используйте JSON.parse
).
Глобальный контекст шифрования это тот, который используется по умолчанию, и настраивается с помощью конфигурационных настроек в application.rb
или конфигурационных файлах среды.
config.active_record.encryption.key_provider = ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new
config.active_record.encryption.encryptor = MyEncryptor.new
Можно переопределить параметр контекста шифрования, передав их в объявлении атрибута:
class Attribute
encrypts :title, encryptor: MyAttributeEncryptor.new
end
Можно использовать ActiveRecord::Encryption.with_encryption_context
для настройки контекста шифрования для заданного блока кода:
ActiveRecord::Encryption.with_encryption_context(encryptor: ActiveRecord::Encryption::NullEncryptor.new) do
...
end
Можно запустить код без шифрования:
ActiveRecord::Encryption.without_encryption do
...
end
Это означает, что чтение зашифрованного текста вернет шифровку, а сохраняемое содержимое будет храниться незашифрованным.
Можно запустить код без шифрования, но предотвращая перезапись зашифрованного содержимого:
ActiveRecord::Encryption.protecting_encrypted_data do
...
end
Это удобно, если вы хотите защитить зашифрованные данные, в то же время запуская произвольный код для них (например, в консоли Rails).