Криптографические поля в ORM
Криптографические поля позволяют автоматически шифровать данные при записи в базу данных и расшифровывать их при чтении. Это избавляет от необходимости вручную реализовывать шифрование при работе с конфиденциальной информацией: токенами, ключами доступа, персональными данными.
В ORM есть два типа криптографических полей:
-
CryptoField— базовый тип, который обеспечивает шифрование и расшифровку, -
SecretField— расширениеCryptoField, которое автоматически генерирует случайное значение, если поле не заполнено при создании записи.
Если нужно только шифровать существующие данные — используйте CryptoField. Если требуется гарантированно иметь уникальное секретное значение для каждой записи — используйте SecretField.
Совет
Подробнее о типах полей в статье Концепция ORM
Настроить ключ шифрования
Ключ шифрования указывается в файле конфигурации ядра /bitrix/.settings.php. В новых дистрибутивах система генерирует ключ автоматически. Если ключа нет, добавьте его вручную. Без ключа криптографические поля работать не будут.
Ключ может быть любой уникальной строкой. Рекомендуем создавать ключ длиной 32 символа из букв латинского алфавита и цифр. Можно сгенерировать с помощью \Bitrix\Main\Security\Random::getString(32).
Внимание
Не меняйте ключ после того, как зашифровали данные. Расшифровать их будет невозможно.
Проверить доступность шифрования
Перед тем как использовать криптографические поля убедитесь, что шифрование доступно. Метод CryptoField::cryptoAvailable() проверяет:
-
наличие ключа в
.settings.php, -
поддержку OpenSSL на сервере.
if (\Bitrix\Main\ORM\Fields\CryptoField::cryptoAvailable())
{
// можно работать с CryptoField и SecretField
}
Проверку можно использовать в установщиках модулей и при миграциях. Это поможет избежать ошибок в окружениях, где шифрование не настроено.
Подготовить таблицы
Зашифрованные данные занимают больше места, чем исходные. Перед тем как начать использовать криптографические поля, убедитесь, что размер колонки в базе данных позволяет хранить зашифрованные значения.
Длина зашифрованного значения зависит от режима шифрования. Для режима CTR используйте формулу:
Для остальных режимов формула:
-
len— максимальная длина исходных данных в байтах. -
16и32— размеры служебных блоков: вектор и хеш. -
1.5— примерный коэффициент увеличения при кодировке base64.
Пример. Если поле занимает до 10 байт, минимальный размер колонки должен быть около 96 байт.
Поле CryptoField
Поле CryptoField автоматически шифрует значение при записи и расшифровывает его при чтении. Используйте его для защиты существующих данных без изменения логики работы с ними. Пустые значения не шифруются и сохраняются как есть.
Объявить поле в классе таблицы
DataManager описывает структуру таблицы в ORM. Чтобы зашифровать конкретную колонку, укажите для нее тип crypto в методе getMap().
Напрямую указывать тип crypto можно только:
-
для новых таблиц и колонок,
-
колонок, в которых данные точно зашифрованы.
Внимание
Поле не может одновременно работать с зашифрованными и незашифрованными данными.
Поэтому в описании поля обычно используют проверку. Метод cryptoEnabled('Field') проверяет, включено ли шифрование для указанного поля.
-
true— используется типcrypto, -
false— обычная строка.
Это позволяет переключаться между режимами, например, во время миграции.
final class BookTable extends \Bitrix\Main\ORM\Data\DataManager
{
public static function getTableName()
{
return 'book';
}
public static function getMap()
{
return [
'ID' => [
'data_type' => 'integer',
'primary' => true,
'autocomplete' => true,
],
// Используем динамический тип данных для колонки ISBN
'ISBN' => [
'data_type' => (static::cryptoEnabled('ISBN') ? 'crypto' : 'string'),
],
];
}
}
В объектном стиле используйте атрибут crypto_enabled.
new \Bitrix\Main\ORM\Fields\CryptoField('ISBN', [
'crypto_enabled' => static::cryptoEnabled('ISBN'),
]);
Включить шифрование
После того как таблица создана и все данные зашифрованы, нужно сообщить ORM, что колонка теперь зашифрована. Для этого вызовите метод enableCrypto().
Метод сохранит информацию о шифровании в служебных опциях таблицы. После этого метод cryptoEnabled('ISBN') будет возвращать true.
Если таблица только создается и в ней еще нет данных, сразу включите шифрование для нужной колонки в установщике модуля.
После этого все операции вставки через DataManager будут автоматически шифровать значение ISBN.
Мигрировать существующие данные
Если в таблице уже есть незашифрованные записи:
-
Проверьте доступность шифрования.
-
Увеличьте размер колонки при необходимости.
-
Создайте временный класс-наследник DataManager с типом
cryptoбез проверки. -
Обновите записи, чтобы они перезаписались и зашифровались.
-
Вызовите метод
enableCrypto()оригинальной таблицы.
use Bitrix\Main\Application;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\CryptoField;
// Временный класс для принудительного шифрования при обновлении
class TempBookTable extends DataManager
{
public static function getTableName()
{
return 'book';
}
public static function getMap()
{
return [
'ID' => [
'data_type' => 'integer',
'primary' => true,
'autocomplete' => true,
],
'ISBN' => [
'data_type' => 'crypto', // всегда шифруем
],
];
}
}
if (CryptoField::cryptoAvailable())
{
$connection = Application::getConnection();
$result = $connection->query('SELECT ID, ISBN FROM book');
// Перебираем все записи и обновляем ISBN через временный класс
while ($row = $result->fetch())
{
// Передаем то же значение, но поле crypto зашифрует его
TempBookTable::update($row['ID'], ['ISBN' => $row['ISBN']]);
}
// После того как все данные зашифрованы, включаем режим шифрования для оригинальной таблицы
BookTable::enableCrypto('ISBN');
}
После этого все данные в колонке ISBN будут зашифрованы, и можно использовать обычный BookTable с проверкой cryptoEnabled.
Для больших таблиц выполняйте обновление частями и вызывайте enableCrypto() только после завершения обработки всех записей.
Внимание
Во время миграции ограничьте доступ к таблице, чтобы избежать чтения уже зашифрованных значений как обычных строк.
Поле SecretField
Поле SecretField наследует поведение CryptoField и добавляет автоматическую генерацию значения, если поле не заполнено. Поле гарантирует, что у каждой записи будет секретное значение.
-
Перед шифрованием
SecretFieldкодирует данные в base64. -
Затем шифрует данные как
CryptoField. -
При чтении расшифровывает и декодирует из base64.
Объявить поле в классе таблицы
В карте таблицы объявите поле как SecretField. Вы можете задать длину генерируемого секрета через параметр secret_length. По умолчанию длина — 20 байт.
use Bitrix\Main\ORM\Fields\SecretField;
public static function getMap()
{
return [
// ...
// другие поля
// ...
'API_TOKEN' => new SecretField('API_TOKEN', [
'secret_length' => 32,
]),
];
}
Если при добавлении записи не указать значение для API_TOKEN, поле заполнится случайной строкой указанной длины. Если передать свое значение, оно сохранится в зашифрованном виде.
При чтении значение автоматически расшифровывается.
Включить шифрование
Как и для CryptoField, после создания таблицы или добавления поля с уже существующими данными, нужно включить шифрование с помощью enableCrypto().
Если таблица новая и пустая, вызовите enableCrypto() сразу после ее создания, например, в установщике модуля.