Привет, Хабр! На связи снова Антон Дятлов, инженер по защите информации в Selectel. Буквально несколько дней назад мы с вами рассмотрели установку и безопасную настройку pgcrypto и изучили его основные возможности. Пришло время перейти к практическому применению этих знаний.

В этой статье разберем конкретные сценарии использования pgcrypto в реальных проектах и углубимся в вопросы производительности и проблемы индексирования зашифрованных данных. Отдельно я сформулировал чек-лист лучших практик безопасности и сравнил pgcrypto с альтернативными подходами, чтобы вы могли сделать осознанный выбор для своей архитектуры. Прошу под кат!

Используйте навигацию, если не хотите читать статью полностью:

Практические сценарии: применяем pgcrypto в реальных задачах

Рассмотрим несколько жизненных примеров, демонстрирующих использование pgcrypto в проектах. Для начала необходимо убедиться, что столбец для зашифрованных данных имеет тип bytea, а ключ надежно хранится во внешнем сервисе — например, KMS.

Засекречивание столбца с конфиденциальными данными

Чтобы зашифровать данные перед вставкой, их нужно преобразовать в байты и передать в функцию encrypt.

INSERT INTO tabl (username, api_key)
VALUES ('sec_user', encrypt(
    convert_to('text_str', 'utf8'), -- Преобразовать ТЕХТ в ВУТЕА
    'ключ шифрования',
    'aes-cbc'
));

Для чтения этих данных потребуется обратная операция — расшифровка с помощью decrypt и преобразование в текст.

SELECT tabl,
    convert_from(decrypt(
        api_key,
        'ключ шифрования',
        'aes-cbc'
    ), 'utf8') AS decrypted_text
FROM tabl
WHERE username = 'sec_user';

Автоматизация с помощью триггеров

Можно создать триггер, чтобы автоматизировать шифрование, однако такой подход замедляет операции вставки и усложняет логику базы данных. Кроме того, так не только повышается нагрузка, но и становится невозможным индексирование на зашифрованных полях.

Его стоит применять с осторожностью, только для особых данных, когда защищенность важнее производительности.

CREATE OR REPLACE FUNCTION encrypt_api_key()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO user_api_keys (username, api_key_encrypted)
    VALUES (
        NEW.username,
        encrypt(convert_to(NEW.api_key, 'utf8'), 'strong-secret-key-123', 'aes-cbc')
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_encrypt_api_key
AFTER INSERT ON user_api_keys_raw
FOR EACH ROW
EXECUTE FUNCTION encrypt_api_key();

Безопасное хранение хешированных паролей

Для хранения паролей используется связка функций crypt и gen_salt. Их совместная работа гарантирует, что даже одинаковые пароли всегда будут иметь разные хеши.

INSERT INTO tabl (username, password_hash)
VALUES (
    'user',
    crypt('password', gen_salt('bf', 10)) -- cost = 10
);

Контроль целостности и подлинности

Для простой проверки невредимости данных можно сохранять их хеш, вычисленный функцией digest.

INSERT INTO config (column, hashcolumn_sha256)
VALUES ('config_data', encode(digest('config_data', 'sha256'), 'hex'));

Более надежный метод — hmac, который не только проверяет целостность, но и подтверждает подлинность данных с помощью секретного ключа:

SELECT *,
    encode(hmac(data::TEXT, 'sec_key', 'sha256'), 'hex') = hmac_signature AS is_valid
FROM config;

Такой подход можно развить до реализации простого «аудиторского следа», который поможет обнаружить несанкционированные изменения записей.

INSERT INTO sensitive_records (data, hmac_signature)
VALUES (
    '{"name": "usr", "role": "admin"}',
    encode(hmac('{"name": "usr", "role": "admin"}', 'audit', 'sha256'), 'hex')
);

SELECT *,
    encode(hmac(data::text, 'audit', 'sha256'), 'hex') = hmac_signature AS column
FROM tabl;

Передача информации с помощью PGP

Для безопасного обмена данными с внешними сторонами можно использовать PGP. Полученное сообщение расшифровывается приватным ключом при вставке в БД.

INSERT INTO partner_data (encrypted_column, decrypted_column)
VALUES (
    dearmor('-----BEGIN PGP MESSAGE----- XXX -----END PGP MESSAGE-----'),
    pgp_pub_decrypt(
        dearmor('-----BEGIN PGP MESSAGE XXX -----END PGP MESSAGE-----'),
        'key_bin'
    )
);

Производительность и индексирование: компромиссы и решения

Криптография — процесс вычислительно интенсивный, особенно если используем сильные алгоритмы, такие как AES, или асимметрия PGP. Шифрование и дешифрование данных на лету неизбежно сказывается на производительности операций INSERT, UPDATE и SELECT. Перед внедрением pgcrypto в промышленную эксплуатацию обязательно проводите нагрузочное тестирование и бенчмаркинг на реалистичных объемах данных.  

Проблема индексирования

При внедрении поиска в зашифрованных данных мы сталкиваемся с одним из самых серьезных ограничений. Стандартные индексы PostgreSQL — например, B-Tree — работают на основе сравнения значений. Однако качественное шифрование (с использованием IV) является недетерминированным: один и тот же источник каждый раз дает разный шифротекст.

В результате индекс, построенный по столбцу с зашифрованными данными, становится бесполезным для поиска по значению. Он может лишь ускорить сканирование по точному совпадению сырых байтов шифротекста, что практически никогда не требуется.  

Существуют обходные пути, но каждый из них имеет свои недостатки.

Детерминированное шифрование

Можно взять режим шифрования, который не использует IV — например, ECB — или передавать один и тот же IV вручную через encrypt_iv(). В таком случае одинаковый открытый текст всегда будет давать тот же самый шифротекст, что позволит построить по нему индекс.

Важно! Так действовать крайне небезопасно — раскрывается информация о повторяющихся данных, и систему становится уязвимой для анализа.

Хеширование для точного поиска

Рядом с зашифрованным столбцом (encrypted_ssn) можно хранить хеш от его открытого текста (ssn_hash). Индекс строится по столбцу с хешем — так получается выполнять быстрый поиск по точному совпадению.

Однако такой подход несет и риски: он позволяет определить, содержат ли две разные строки одно и то же вхождение. Возникает уязвимость и для словарных атак, если пространство возможных значений невелико.

Поиск по диапазону или частичному совпадению по-прежнему неосуществим.

Дешифрация на стороне клиента

Приложение извлекает из базы данных более широкий набор строк, дешифрует их в своей памяти и затем фильтрует. Такой подход безопасен, но может быть крайне неэффективным, так как требует передачи больших объемов данных по сети и создает нагрузку на клиентское приложение.  

Вывод

Наилучшая стратегия — избегать шифрования полей, по которым производится частый поиск. Если поиск по зашифрованным данным является критическим требованием, возможно, стоит рассмотреть альтернативные архитектурные решения — например, специализированные системы токенизации.

Лучшие практики безопасности: как избежать типовых ошибок

Для обеспечения надежной защиты при использовании pgcrypto придерживайтесь следующего чек-листа.

Управление ключами. Применяйте внешний KMS. Это не рекомендация, а практически обязательное требование для промышленных систем. Внедрите политики регулярной ротации ключей.  

Выбор алгоритмов. Опирайтесь только на современные и проверенные алгоритмы: AES-256 для шифрования, SHA-256 или SHA-512 для хеширования и bcrypt (bf) для паролей. Категорически избегайте устаревших DES, 3DES, MD5, SHA-1 и режима шифрования ECB.  

Соли и векторы инициализации (IV). Всегда должны применяться неповторяющиеся, криптографически случайные соли для хеширования паролей (gen_salt). Убедитесь, что для симметричного шифрования используются уникальные IV для каждой операции (что pgcrypto делает по умолчанию в режиме CBC). Никогда не повторяйте и не хардкодьте эти значения.  

Принцип наименьших привилегий. Прибегайте к выделенным схемам и точечной настройке прав через GRANT и REVOKE, чтобы ограничить доступ к криптографическим функциям только тем ролям, которым это абсолютно необходимо.  

Гибридный подход. Рассмотрите модель «шифрование в приложении, хранение в БД». В этой схеме приложение шифрует данные перед отправкой в базу. PostgreSQL хранит уже непрозрачные для него данные. Такой подход защищает информацию даже от скомпрометированного суперпользователя СУБД, но усложняет логику приложения.  

Аудит. Журналируйте все операции доступа к чувствительным данным и вызовы функций дешифрования. Это поможет своевременно обнаружить аномальную активность, которая может свидетельствовать об атаке или утечке.

Сравнение с альтернативами: когда pgcrypto — лучший выбор

pgcrypto — это один из многих инструментов в арсенале защиты данных. Выбор конкретного решения зависит от вашей модели угроз, требований к производительности и архитектурных ограничений. Рассмотрим основные подходы.  

Шифрование на уровне столбцов с помощью pgcrypto

Такой подход заключается в шифровании конкретных полей непосредственно внутри базы данных. Он эффективно защищает от угроз, связанных с физическим допуском к устройствам, — например, кражи дисков или резервных копий. Однако, если ключи шифрования доступны приложению, pgcrypto не спасет от SQL-инъекций или от скомпрометированного суперпользователя. 

Этот метод лучше всего подходит для гранулярной защиты отдельных, особо чувствительных полей — например, ПДн или секретов, — позволяя сохранить высокую производительность для операций с остальными данными.  

Шифрование на уровне приложения

В этой модели приложение берет на себя задачу шифрования данных перед их отправкой в базу. Для СУБД они — непрозрачный набор байтов. Метод обеспечивает более высокий уровень защиты, так как оберегает информацию даже от скомпрометированного администратора.

Тем не менее, подход не защищает от уязвимостей в самом приложении или от компрометации хоста, на котором оно запущено. Шифрование на уровне приложения — выбор для систем с максимальными требованиями к безопасности, где необходимо строгое разделение ролей администратора БД и владельца данных.

Прозрачное шифрование данных (TDE)

TDE (Transparent Data Encryption) работает на уровне файловой системы, шифруя все файлы базы данных целиком — например, с помощью инструментов вроде LUKS в Linux. Этот метод защищает исключительно от физической кражи носителей информации. Он бессилен против любого атакующего, получившего доступ к запущенной и смонтированной базе данных, — будь то через SQL-инъекция или вход с правами администратора.

TDE — хороший выбор для выполнения формальных требований комплаенса и защиты от физических угроз с минимальными изменениями в коде приложения.  

Токенизация и внешние хранилища (Vaulting)

Этот подход предполагает замену чувствительных данных в основной базе на неконфиденциальные «токены». Сами данные при этом лежат в отдельном, высокозащищенном хранилище (Vault). Токенизация обеспечивает наивысший уровень изоляции данных и значительно сокращает область действия стандартов комплаенса, таких как PCI DSS.

К недостаткам можно отнести сложность интеграции и сетевые задержки при обращении к внешнему хранилищу. Метод идеально подходит для работы с данными платежных карт и другими критически важными сведениями, требующими максимальной изоляции.  

pgcrypto является оптимальным выбором, когда необходимо гранулярное шифрование на уровне столбцов и вы готовы взять на себя ответственность за управление ключами. Потребуется реализовать защиту данных в рамках существующей инфраструктуры PostgreSQL как часть эшелонированной обороны.

Ограничения и важные нюансы

При работе с pgcrypto необходимо четко осознавать все ограничения.

Управление ключами — главная проблема и зона ответственности пользователя. Потеря ключа означает безвозвратную утрату данных.  

Шифрование — затратная операция, что особенно заметно при работе с PGP и большими данными. Используйте его только для тех данных, которые действительно этого требуют, и всегда проводите тестирование производительности.  

Полноценный поиск по зашифрованным данным невозможен. Все обходные пути сопряжены с компромиссами в безопасности или производительности.  

Данные в бэкапах и на репликах остаются зашифрованными. Это хорошо для безопасности, но критически важно обеспечить доступность ключей для процессов восстановления и для серверов-реплик. Потеря ключа сделает резервную копию бесполезной.  

Шифрование — не панацея. Оно защищает данные в состоянии покоя, но не спасает от всех угроз. Уязвимости в приложении, SQL-инъекции или скомпрометированные учетные записи пользователей с правами на дешифровку по-прежнему представляют серьезную опасность.

Заключение

pgcrypto — мощный и гибкий инструментарий, интегрированный непосредственно в ядро PostgreSQL. Он предоставляет разработчикам и администраторам все необходимые примитивы для решения ключевых криптографических задач: обеспечения конфиденциальности, целостности и аутентификации данных на уровне БД.  

Однако pgcrypto — не решение «из коробки». Его эффективность целиком и полностью зависит от ясного и четкого понимания имеющихся возможностей, ограничений и дисциплинированного подхода к основам безопасности. Главный вывод, который должен сделать каждый, кто планирует использовать это расширение, заключается в следующем: вся ответственность за управление жизненным циклом ключей шифрования лежит на вас.

Используйте pgcrypto разумно — как один из важных слоев в комплексной стратегии эшелонированной обороны. Сочетайте его с безопасностью на уровне сети (SSL/TLS), контролем доступа к базе данных, надежными практиками разработки приложений и строгими политиками аудита. Только в этом случае pgcrypto станет не источником ложного чувства безопасности, а действительно надежным элементом защиты ваших данных.

Прежде чем внедрять pgcrypto в промышленные системы, тщательно изучите официальную документацию PostgreSQL и протестируйте все аспекты его работы на специализированных стендах.

Комментарии (0)