
Привет, Хабр! На связи Антон Дятлов, инженер по защите информации в Selectel. Хранение конфиденциальных данных в PostgreSQL в открытом виде — мина замедленного действия. Неприятности в будущем становятся неизбежными. Достаточно одной успешной SQL‑инъекции, утечки резервной копии или компрометации доступа к серверу, чтобы вся чувствительная информация — от персональных данных пользователей до API-ключей — оказалась в руках злоумышленников.
Даже если все обошлось, то принимать меры защиты все равно придется. Исправлять БД постфактум — задача неблагодарная и крайне рискованная. Шифрование уже существующих данных потребует сложной миграции. Это долго, дорого и не всегда проходит гладко.
К счастью, большинства этих проблем можно избежать, если подойти к безопасности данных осознанно с самого начала. PostgreSQL предлагает для этого мощный встроенный инструмент — расширение pgcrypto. Эта статья — небольшое руководство по его правильному и безопасному использованию.
Введение. Стратегия защиты данных на уровне СУБД
pgcrypto — доверенное расширение для PostgreSQL, добавляющее набор криптографических функций непосредственно в SQL. Модуль позволяет выполнять хеширование и шифрование данных внутри самой БД, что делает его мощным инструментом.
Внедрение шифрования на уровне БД продиктовано современными требованиями безопасности и необходимостью противостоять сложным угрозам. Основные причины для использования pgcrypto можно свести к нескольким ключевым стратегическим преимуществам.
Защита данных в состоянии покоя (data-at-rest) — основная задача, которую решает pgcrypto. Даже если злоумышленник получит физический доступ к файлам БД — например, через кражу серверного диска, резервной копии или компрометацию файловой системы — зашифрованные данные останутся для него нечитаемым набором байтов. Расшифровка возможна только при наличии ключа, который должен храниться отдельно.
Соответствие требованиям (compliance) — важно для некоторых компаний. Многие отраслевые и государственные стандарты безопасности, такие как PCI DSS (для платежных карт), GDPR (общие правила защиты данных в ЕС) и HIPAA (для медицинских данных в США), прямо требуют шифрования чувствительной информации в состоянии покоя. Расширение предоставляет прямой и аудируемый способ выполнить эти требования для конкретных полей в базе данных, что упрощает прохождение проверок.
Гибкость и удобство pgcrypto позволяет применять гранулярное, или выборочное шифрование. Можно зашифровать только самые критичные столбцы в таблице — например, social_security_number
. Остальные поля при этом могут оставаться в открытом виде — например, username
или registration_date
. Такой подход позволяет сохранить высокую производительность для операций поиска и индексации по неконфиденциальным данным, применяя ресурсоемкие криптографические операции только там, где это действительно необходимо.
Последний эшелон обороны на случай если атака на приложение увенчалась успехом. Представим, что злоумышленник получил возможность выполнять SQL-запросы, возможно, даже с повышенными привилегиями. В этом сценарии без доступа к ключам шифрования, которые хранятся вне самой БД, атакующий не сможет извлечь из таблиц никакие чувствительные данные.
Однако важно с самого начала понимать фундаментальный принцип работы с pgcrypto. Расширение предоставляет криптографические примитивы — то есть сами функции для шифрования, дешифрования и хеширования, — но не предлагает готового, управляющего решения. Безопасность всей системы целиком и полностью зависит от того, насколько грамотно выстроены процессы, особенно в части работы с ключами шифрования. Простота установки pgcrypto обманчива; настоящая сложность — в его правильном и безопасном использовании.

Установка и безопасная настройка: закладываем фундамент
Прежде чем использовать pgcrypto, необходимо убедиться, что расширение установлено, и правильно настроить права доступа.
Проверить установку можно, подключившись к необходимой базе данных с помощью клиента psql
, после чего выполнить команду:
\dx pgcrypto
Если расширение уже установлено, появится информация о его версии и описание.
List of installed extensions
Name | Version | Schema | Description
---------+---------+--------+-------------------------
pgcrypto| 1.3 | crypto | cryptographic functions
Выполнить установку расширения можно, подключившись к нужной базе данных и выполнив команду:
CREATE EXTENSION pgcrypto SCHEMA crypto;
Обратите внимание: необходимо обладать правами CREATE для базы данных или быть суперпользователем.
Ключевой момент здесь — использование опции SCHEMA crypto
. По умолчанию PostgreSQL установил бы все функции расширения в общедоступную схему public
. Размещение pgcrypto в отдельной, изолированной схеме — например, crypto
— важный вопрос организации. Такой подход создает четкую границу безопасности и позволяет реализовать принцип наименьших привилегий.
До версии 9.1, pgcrypto был частью пакета
contrib
и требовал запуска скриптов для работы. МетодCREATE EXTENSION pgcrypto
вытеснил этот подход и является лучшей практикой для установки расширения.
После установки в отдельную схему следует ограничить доступ к криптографическим функциям. Для этого предпримем следующие шаги.
Шаг 1. Для обеспечения безопасности доступа необходимо явно выдавать право GRANT
на выполнение:
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA crypto FROM PUBLIC;
Шаг 2. Отзываем все права у роли PUBLIC
. Теперь ни один пользователь по умолчанию не сможет вызывать функции pgcrypto.
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA crypto FROM PUBLIC;
Шаг 3. Предоставляем права выборочно. Можно создать специальные роли для приложений или пользователей, которым необходим доступ к шифрованию. После этого выдаем ролям права только на те функции, которые им действительно необходимы. Например, для роли secure_user
, которой нужно шифровать и дешифровывать данные:
GRANT EXECUTE ON FUNCTION crypto.encrypt(bytea, bytea, text) TO secure_user;
GRANT EXECUTE ON FUNCTION crypto.decrypt(bytea, bytea, text) TO secure_user;
Такая точечная настройка предотвращает несанкционированное использование мощных криптографических инструментов. Например, можно запретить приложению доступ к функциям генерации ключей или хеширования паролей, если его задача только шифрование данных.
Ключевые возможности: от хеширования до симметричного шифрования
Расширение предлагает три основные группы функций: хеширование, симметричное и асимметричное шифрование.
Хеширование для проверки целостности
В pgcrypt для этой цели предлагается две функции: digest()
и hmac()
.
Хеширование — создание уникального, необратимого и короткого «отпечатка» (хеша) для произвольного набора данных. Ее основное предназначение — проверка целостности. Можно сохранить хеш файла или строки конфигурации, а позже пересчитать его и сравнить с эталоном. Любое изменение в исходных данных приведет к совершенно другому хешу.
Функция digest(data, type)
вычисляет криптографический хеш. В примере ниже — вычисление хеша SHA-256 для текстовой строки:
SELECT digest('some sensitive data', 'sha256');
Функция hmac(data, key, type)
вычисляет HMAC (Keyed-Hash Message Authentication Code) — хеш с использованием секретного ключа. HMAC решает более сложную задачу: не только проверяется целостность данных, но и подтверждается их подлинность. Появляется ответ на вопрос: «Эти данные не изменились, и они пришли именно от того, кто знает секретный ключ?» Одним словом, hmac
— идеальный инструмент для защиты API-сообщений или создания защищенных от подделки аудиторских записей.
При выборе алгоритма хеширования (type
) следует использовать современные и надежные варианты, такие как sha256
или sha512
. Например, md5
и sha1
считаются устаревшими и уязвимыми для коллизий, их использование в новых системах недопустимо.
Важное предупреждение
Никогда не используйте функцию
digest()
для хранения паролей. Она не применяет «соль» (salt
), а значит, одинаковые пароли всегда будут иметь одинаковый хеш. Для паролей в pgcrypto есть специальный набор функций.
Симметричное шифрование
Функции encrypt()
и decrypt()
предназначены для симметричного шифрования. При этом используется один и тот же секретный ключ, как для прямого, так и для обратного преобразования:
encrypt(data, key, type)
шифрует данныеdata
с помощью ключаkey
и алгоритмаtype
;decrypt(data, key, type)
дешифрует данныеdata
с помощью того же ключаkey
и алгоритмаtype
.
Рекомендуемым стандартом для симметричного шифрования сегодня является AES
с длиной ключа 256 бит. Длина секретного ключа всегда определяется используемым алгоритмом. Например, AES-256 требует ключ длиной ровно 32 байта. Тогда для генерации следует воспользоваться функций gen_random_bytes(32)
.
Поддерживаются также и другие алгоритмы (параметр type
): DES, 3DES, AES-128, 192, 256, BF.
Безопасность шифрования зависит не только от стойкости самого алгоритма, но и от режима, в котором он используется. Именно он определяет, как обрабатываются блоки данных. В pgcrypto доступны несколько режимов, включая CBC, CFB, OFB и ECB. Рассмотрим их чуть подробнее.
Режим ECB (Electronic Codebook) — самый простой и самый небезопасный. Он шифрует каждый блок данных независимо друг от друга. Одинаковые блоки открытого текста всегда преобразуются в одинаковые блоки шифротекста. Такой подход раскрывает структуру данных и позволяет проводить статистический анализ. Использование ECB категорически не рекомендуется.
Режим CBC (Cipher Block Chaining) значительно более безопасный. Перед шифрованием каждый блок открытого текста комбинируется с предыдущим зашифрованным блоком с помощью функции XOR. Чтобы сделать этот процесс случайным для самого первого блока, применяется вектор инициализации (IV, Initialization Vector) — уникальное, случайное значение.
Роль IV критически важна. Его можно рассматривать как «соль для шифрования». Из‑за IV, даже если тем же самым ключом несколько раз зашифровать один и тот же текст, каждый раз будет получаться совершенно разный результат. Злоумышленник лишается возможности анализировать повторяющиеся паттерны.
pgcrypto автоматически генерирует и использует случайный IV для режимов, которые того требуют, — например,
aes-cbc
. Однако повторное использование IV с тем же ключом — катастрофическая ошибка, которая может полностью скомпрометировать шифрование. Она позволяет злоумышленнику, который контролирует часть шифруемых данных, восстановить другие части сообщения.Для случаев, когда требуется совместимость с другими системами или явный контроль над вектором инициализации, pgcrypto предоставляет функции
encrypt_iv()
иdecrypt_iv()
, которые позволяют задавать IV вручную.
В режиме OFB(Output Feedback) генерируется случайный поток, который выполняет операцию XOR с данными.
CFB(Cipher Feedback) — шифруется первый блок, его выход выполняет операцию XOR с первым блоком открытого текста и так далее.
CTR(Counter Mode) — каждый блок данных шифруется с использованием уникального значения.
В pgcrypto используется дополнение (padding) — добавление лишних данных к открытому тексту перед шифрованием, чтобы длина стала кратной размеру блока. Таким образом, выполняются требования алгоритма шифрования.
При использовании режимов CBC, ECB, OFB, CFB применяется padding PKCS#7
.
Когда шифруется строка, которая меньше блока, недостающие байты добавляются как 0x0A. После дешифровки последний байт 0x0A интерпретируется как количество добавленных байт, и они удаляются.
Управление ключами: главная зона ответственности
Здесь сосредоточен один из самых важных аспектов безопасного использования pgcrypto. Расширение не предоставляет никаких средств для управления ключами шифрования — оно лишь использует те, что ему передаются.
Хранение ключей в той же базе данных, что и зашифрованные данные, особенно в открытом виде, полностью обесценивает шифрование. Подобный подход несет несколько неудобств и рисков:
если злоумышленник получит доступ к базе, у него окажется и замок, и ключ;
при хранении ключа в БД, его затруднительно будет обновить без перешифрования всех данных;
постоянно нависает угроза внутренней атаки, когда администратор БД имеет возможность читать зашифрованные данные.
Рассмотрим основные стратегии хранения ключей.
Специализированные внешние сервисы управления ключами, KMS (Key Management Service) — наиболее надежный и рекомендуемый подход. Такие системы, как HashiCorp Vault или облачные решения от AWS, Google и других провайдеров, разработаны специально для безопасного хранения секретов, поддерживают ротацию ключей, аудит доступа и строгий контроль прав.
Преимущества:
хорошая защита от перехвата,
возможность безопасно делиться ключами,
логирование и ограничение доступа.
Недостатки:
зависимость от внешних сервисов,
сложность интеграции в БД,
вероятность задержки шифрования и дешифрования.
Переменные окружения — другой способ передачи ключей во время запуска.
Преимущества:
простота для контейнеризованных приложений (Docker, Kubernetes),
разделение конфигурации и кода.
Недостатки:
требуется контроль над средой выполнения,
трудности при масштабировании.
Конфигурационные файлы со строгими правами (0600) — еще один способ хранения ключей, который имеет право на жизнь.
Преимущества:
простота реализации,
легкость смены ключа на одном сервере.
Недостатки:
необходимость введения строгих прав;
трудность синхронизации файлов при масштабировании;
высокий риск случайной компрометации — например, попадание в Git.
Использование отдельных таблиц или инструментов — ключи хранятся в другой БД или внешнем сервисе, которые доступны по API или для конкретного пользователя.
Преимущества:
логирование и контроль доступа,
разграничение прав пользователей для конкретных ключей.
Недостатки:
требуется дополнительная инфраструктура,
добавляется еще один сервис или таблица — единственно для поддержания работы.
Асимметричное шифрование с PGP
В отличие от симметричного шифрования, асимметричное использует пару ключей: публичный и приватный. Данные, зашифрованные публичным ключом, могут быть расшифрованы только соответствующим приватным ключом. В pgcrypto этот механизм реализован через функции, совместимые с протоколом PGP (Pretty Good Privacy).
Плюсы данного протокола в том, что он поддерживает шифрование сообщений, обмен данными между пользователями, а также цифровые подписи. Основной сценарий использования — безопасный обмен данными, часто с внешними сторонами. Можно безбоязненно передавать свой публичный ключ партнеру, который сможет только зашифровывать данные. Расшифровать и прочитать их сможет исключительно владелец приватного ключа.
Шифрование бэкапов, секретов и чувствительной информации, безопасная переписка — всех сценариев использования PGP не перечислить.
Ключевые функции PGP в pgcrypto:
pgp_pub_encrypt(data text, key bytea [,...])
— шифрует данные с помощью публичного PGP-ключа;pgp_pub_decrypt(msg bytea, key bytea [,...])
— расшифровывает сообщение с помощью приватного PGP-ключа;pgp_sym_encrypt(data text, psw text [,...])
— шифрует данные, но с помощью симметричного шифрования и парольной фразы;pgp_sym_decrypt(msg bytea, psw text [,...])
— расшифровывает сообщение также с помощью симметричного дешифрования и парольной фразы.
Сами PGP-ключи не могут быть сгенерированы внутри PostgreSQL. Для их создания необходимо использовать внешние утилиты — такие, как GnuPG (gpg
). Приведем небольшой пример — получение новой пары ключей RSA размером 4 096 бит.
1. Устанавливаем необходимое ПО:
apt install gnupg
2. Для создания пары воспользуемся расширенной версией ‑‑generate-key
. Значения всех неуказанных, но необходимых параметров — например, алгоритм, размер ключа, срок его действия, а также примечание, имя или адрес электронной почты — будут уточнены в диалоге:
gpg --full-generate-key
3. Для экспорта сгенерированных ключей необходимо воспользоваться опцией ‑‑export
.
Ключи часто экспортируются в текстовом формате ASCII-armored (с помощью RADIX-64). Для этого существует опция --armor
. Перед использованием в функциях pgcrypto такой ключ необходимо преобразовать из текста в тип bytea
с помощью функции dearmor()
.
Важно! В pgcrypto поддерживаются только шифрование и дешифрование PGP. Функции для создания и проверки цифровых подписей PGP в расширении отсутствуют.
Если пользователей, присылающих сообщения, несколько, то можно извлечь ID ключа из зашифрованных данных с помощью команды:
pgp_key_id()
Затем останется выбрать нужный приватный ключ для расшифровки.
Безопасное хранение паролей и генерация случайных данных
pgcrypto предоставляет специализированный и правильный инструментарий для работы с паролями пользователей.
Для хеширования паролей предназначены функции crypt()
и gen_salt()
. Посмотрим на пример вставки хеша пароля в таблицу пользователей:
INSERT INTO users (username, password_hash)
VALUES ('new_user', crypt('highly-secure-password123', gen_salt('bf', 10)));
Здесь происходит следующее.
Функция
gen_salt('bf', 10)
генерирует уникальную случайную соль для алгоритмаbcrypt
(bf
). Соль гарантирует, что даже, если два пользователя выберут одинаковые пароли, их хеши в базе данных будут разными.Параметр 10 — фактор трудоемкости (cost factor). Он определяет, насколько ресурсоемким будет вычисление хеша. Чем выше значение, тем медленнее работает хеширование и тем сложнее злоумышленнику перебирать пароли методом грубой силы (brute-force). Для
bcrypt
рекомендуются значения от 10 до 12.Функция
crypt()
принимает пароль и сгенерированную соль и возвращает готовый хеш для хранения. Для проверки пароля при входе пользователя используется она же, но в качестве второго аргумента передается уже существующий хеш из базы.
SELECT password_hash = crypt('entered-password', password_hash)
FROM users WHERE username = 'new_user';
Для генерации случайных данных представлены следующие функции.
gen_random_bytes(count)
выдает криптографически стойкую последовательность случайных байтов. Это идеальный инструмент для создания ключей шифрования, векторов инициализации (IV) или любых других секретов. Однако существует ограничение: за один вызов можно сгенерировать не более 1 024 байт — так предотвращается быстрое исчерпание энтропии в системе.gen_random_uuid()
создает стандартный UUID версии 4, основанный на случайных числах. Отлично подходит для создания уникальных первичных ключей в таблицах или идентификаторов транзакций.
Заключение
Мы познакомились с базовыми возможностями pgcrypto: установкой расширения, хешированием данных с помощью digest()
и hmac()
, симметричным шифрованием AES-256, асимметричным PGP и генерацией случайных данных. А также узнали, почему важно защищать данные на уровне БД, как управлять доступом к функциям и где хранить ключи, чтобы минимизировать риски.
Во второй части углубимся в практические аспекты:
реальные сценарии использования;
вопросы производительности,
лучшие практики безопасности,
сравнение с альтернативами,
наконец, ограничения pgcrypto.
В следующий раз также перейдем от теории к практике. Посмотрим, как интегрировать pgcrypto в реальные проекты, избежать типичных ошибок и комбинировать расширение с другими инструментами безопасности.
Akina
Не всегда есть под рукой psql. Почти то же самое можно получить и на чистом SQL. Что-то вроде
Описание в принципе тоже где-то валяется, но искать лень...