Криптографические поля в ORM
Криптографические поля позволяют автоматически шифровать данные при записи в базу данных и расшифровывать их при чтении. Это избавляет от необходимости вручную реализовывать шифрование при работе с конфиденциальной информацией: токенами, ключами доступа, персональными данными.
В ORM есть два типа криптографических полей:
-
CryptoField— базовый тип, который обеспечивает шифрование и расшифровку, -
SecretField— расширениеCryptoField, которое автоматически генерирует случайное значение, если поле не заполнено при создании записи.
Если нужно только шифровать существующие данные — используйте CryptoField. Если требуется гарантированно иметь уникальное секретное значение для каждой записи — используйте SecretField.
Подробнее о типах полей в статье Концепция ORM
Настроить ключ шифрования
Ключ шифрования указывается в файле конфигурации ядра /bitrix/.settings.php. В новых дистрибутивах система генерирует ключ автоматически. Если ключа нет, добавьте его вручную. Без ключа криптографические поля работать не будут.
'crypto' => [
'value' => [
'crypto_key' => 'mysupersecretphrase',
],
'readonly' => true,
],
Ключ может быть любой уникальной строкой. Рекомендуем создавать ключ длиной 32 символа из букв латинского алфавита и цифр. Можно сгенерировать с помощью \Bitrix\Main\Security\Random::getString(32).
Не меняйте ключ после того, как зашифровали данные. Расшифровать их будет невозможно.
Проверить доступность шифрования
Перед тем как использовать криптографические поля убедитесь, что шифрование доступно. Метод CryptoField::cryptoAvailable() проверяет:
-
наличие ключа в
.settings.php, -
поддержку OpenSSL на сервере.
if (\Bitrix\Main\ORM\Fields\CryptoField::cryptoAvailable())
{
// можно работать с CryptoField и SecretField
}
Проверку можно использовать в установщиках модулей и при миграциях. Это поможет избежать ошибок в окружениях, где шифрование не настроено.
Подготовить таблицы
Зашифрованные данные занимают больше места, чем исходные. Перед тем как начать использовать криптографические поля, убедитесь, что размер колонки в базе данных позволяет хранить зашифрованные значения.
Длина зашифрованного значения зависит от режима шифрования. Для режима CTR используйте формулу:
newlen = (len + 16 + 32) * 1.5
Для остальных режимов формула:
newlen = (len + (16 - len % 16) + 16 + 32) * 1.5
-
len— максимальная длина исходных данных в байтах. -
16и32— размеры служебных блоков: вектор и хеш. -
1.5— примерный коэффициент увеличения при кодировке base64.
Пример. Если поле занимает до 10 байт, минимальный размер колонки должен быть около 96 байт.
Поле CryptoField
Поле CryptoField автоматически шифрует значение при записи и расшифровывает его при чтении. Используйте его для защиты существующих данных без изменения логики работы с ними. Пустые значения не шифруются и сохраняются как есть.
Объявить поле в классе таблицы
DataManager описывает структуру таблицы в ORM. Чтобы зашифровать конкретную колонку, укажите для нее тип crypto в методе getMap().
'ISBN' => [
'data_type' => 'crypto',
],
Напрямую указывать тип 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().
BookTable::enableCrypto('ISBN');
Метод сохранит информацию о шифровании в служебных опциях таблицы. После этого метод cryptoEnabled('ISBN') будет возвращать true.
Если таблица только создается и в ней еще нет данных, сразу включите шифрование для нужной колонки в установщике модуля.
if (\Bitrix\Main\ORM\Fields\CryptoField::cryptoAvailable())
{
BookTable::enableCrypto('ISBN');
}
После этого все операции вставки через 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().
if (\Bitrix\Main\ORM\Fields\CryptoField::cryptoAvailable())
{
MyTable::enableCrypto('API_TOKEN');
}
Если таблица новая и пустая, вызовите enableCrypto() сразу после ее создания, например, в установщике модуля.