Шифрование персональных данных с надёжным хранением ключей.

В современных ИТ-системах конфиденциальные данные, как правило, хранятся в базах данных. А ещё практически любая система требует от пользователей его персональные данные: телефон, почта и др. Одним словом — ИСПДн.

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

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

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

Репозиторий на Гитхабе: https://github.com/laboratory50/pgsodium-tpm

Что такое PgSodium?

PgSodium — это расширение PostgreSQL, предоставляющее криптографические функции на основе библиотеки libsodium. В отличие от встроенного расширения pgcrypto, PgSodium предлагает более современные и безопасные алгоритмы, включая криптографию с открытым ключом, аутентифицированное шифрование и безопасное управление ключами.

Основные возможности PgSodium:

  • Шифрование/расшифровка: шифрование данных в столбцах (например, типа bytea) с использованием симметричных ключей.
  • Управление ключами: создание, хранение и использование криптографических ключей.
  • Хэширование и подписи: функции для безопасного хранения паролей и проверки целостности данных.
  • Ролевая модель: разделение прав доступа (например, роли pgsodium_keymaker, pgsodium_keyholder) для контроля над ключами.

PgSodium предоставляет два варианта использования:

  1. Со сторонними ключами: ваше приложение самостоятельно генерирует ключи и передаёт их в функции шифрования.
  2. С внутренние ключами: первичный ключ (root key) загружается в память сервера PostgreSQL и используются для создания вторичных ключей шифрования.

В данной статье мы рассматриваем вариант использования внутренних ключей, как наиболее применимый на собственной ИТ-инфраструктуре. Почему так? А вот почему:

  • Безопасность: в запросах SQL используются идентификаторы ключей, сами ключи не передаются.
  • Разграничение полномочий: роль pgsodium_keyiduser может только использовать ключи (косвенно по идентификатору), а для создания ключей необходима роль pgsodium_keymaker.
  • Автоматизация: облегчает ротацию ключей и управление жизненным циклом без необходимости переписывать код приложения.
  • Сложные сценарии: упрощает реализацию шифрования «один ключ на пользователя» через триггеры.

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

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

Тестируем PgSodium

1. Контейнер с PostgreSQL

Официальный образ PostgreSQL не содержит PgSodium, поэтому его нужно собрать самостоятельно, добавив библиотеку libsodium и само расширение.

Пример Dockerfile:

FROM postgres:16

# Установка зависимостей для сборки
RUN apt-get update && apt-get install -y \
    libsodium-dev \
    postgresql-server-dev-$PG_MAJOR \
    make \
    gcc \
    git

# Сборка и установка pgsodium
RUN git clone https://github.com/tembo/pgsodium /tmp/pgsodium \
    && cd /tmp/pgsodium \
    && make install \
    && rm -rf /tmp/pgsodium

2. Управления внутренними ключами

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

Создание скрипта получения серверного ключа

В каталог с расширениями (обычно /usr/share/postgresql/$PG_MAJOR/extension/) нужно поместить исполняемый файл pgsodium_getkey. Пример скрипта, генерирующего ключ из /dev/urandom:

#!/bin/sh
# pgsodium_getkey - должен выводить 64-символьную hex-строку (32 байта)
if [ ! -f /var/lib/postgresql/data/pgsodium_root.key ]; then
    head -c 32 /dev/urandom | od -An -vtx1 | tr -d ' \n' > /var/lib/postgresql/data/pgsodium_root.key
fi
cat /var/lib/postgresql/data/pgsodium_root.key

Важно сделать файл исполняемым:

chmod +x /usr/share/postgresql/16/extension/pgsodium_getkey

Подключение в docker-compose.yml

services:
  db:
    build: .
    environment:
      POSTGRES_PASSWORD: your_password
    command: postgres -c 'shared_preload_libraries=pgsodium'
    volumes:
      - ./pgsodium_getkey:/usr/share/postgresql/16/extension/pgsodium_getkey:ro
      - pgdata:/var/lib/postgresql/data

Включение расширения в базе данных

CREATE EXTENSION IF NOT EXISTS pgsodium;

Использование PgSodium

  1. Генерация ключей с использованием первичного ключа:
    -- Генерация вторичного ключа шифрования
    SELECT pgsodium.create_key('my_key');
  2. Шифрование данных на уровне столбцов:
    -- Создание таблицы с зашифрованными данными
    CREATE TABLE users (
        id SERIAL PRIMARY KEY,
        name TEXT,
        email BYTEA, -- зашифрованное поле
        ssn BYTEA    -- зашифрованное поле
    );
    
    -- Шифрование данных при вставке
    INSERT INTO users (name, email, ssn)
    VALUES (
        'Иван Иванов',
        pgsodium.crypto_aead_det_encrypt(
            convert_to('ivan@example.com', 'utf8'),
            convert_to('context', 'utf8'),
            'your_key_id_here'::uuid
        ),
        pgsodium.crypto_aead_det_encrypt(
            convert_to('123-45-6789', 'utf8'),
            convert_to('context', 'utf8'),
            'your_key_id_here'::uuid
        )
    );
    
    -- Расшифровка данных при выборке
    SELECT
        name,
        convert_from(
            pgsodium.crypto_aead_det_decrypt(
                email,
                convert_to('context', 'utf8'),
                'your_key_id_here'::uuid
            ),
            'utf8'
        ) as email
    FROM users;

Проблема хранения серверного ключа и решение с TPM

Остаётся проблема хранения серверного ключа. PgSodium получает ключ при старте сервера с помощью скрипта pgsodium_getkey. Ключ можно создать средствами PgSodium:

select encode(randombytes_buf(32), 'hex')

Однако безопасное хранение ключа — отдельная задача. Распространённые решения: чтение ключа с SSH-сервера, использование облачных систем хранения (Vault, Infisical) или аппаратного модуля безопасности (TPM).

Для тех, кто использует собственную инфраструктуру, наиболее безопасным представляется использование TPM.

Что такое TPM?

TPM (Trusted Platform Module) — это специализированный криптографический процессор, который обеспечивает аппаратную защиту ключей шифрования. Он предоставляет безопасное хранилище для криптографических ключей и выполняет криптографические операции без экспорта ключей в память.

Интеграция TPM с PostgreSQL в Docker

Для работы с TPM в контейнере необходимо подключить реальный или виртуальный TPM и установить пакет для работы с ним.

Дополним Dockerfile:

RUN apt-get update && apt-get install -y tpm2-tools

В docker-compose.yml добавим доступ к TPM:

devices:
  - "/dev/tpm0:/dev/tpm0"
  - "/dev/tpmrm0:/dev/tpmrm0"
privileged: true

Хранение ключа в TPM

В TPM количество хранимых пользовательских данных ограничено (обычно до 7 объектов). Чтобы сохранить ключ в TPM, выполните следующий скрипт:

# 1. Создание случайного ключа длиной 32 байта
openssl rand 32 > root.key

# 2. Создание первичного объекта в TPM (Primary Object)
tpm2_createprimary -C o -c primary.ctx
tpm2_create -C primary.ctx -i root.key -u key.pub -r key.priv
tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx

# 3. Сохранение ключа в NVRAM TPM под постоянным дескриптором (например, 0x81000000)
tpm2_evictcontrol -C o -c key.ctx 0x81000000

# 4. Удаление исходного файла ключа!
rm -f root.key

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

Изменение скрипта pgsodium_getkey для работы с TPM

Чтобы считывать серверный ключ из TPM, изменим файл pgsodium_getkey:

#!/bin/bash -e
# Конфигурация
PERSISTENT_HANDLE="0x81000000" # Дескриптор ключа в TPM

if ! tpm2_getcap handles-persistent 2>/dev/null | grep -q "$PERSISTENT_HANDLE"; then
    echo "ERROR: Key handle $PERSISTENT_HANDLE not found in TPM" >&2
    exit 1
fi

tpm2_unseal -c "$PERSISTENT_HANDLE" 2>/dev/null | hexdump -ve '1/1 "%.2x"'

Таким образом, ключ PgSodium хранится в TPM, а на сервере его нет.

Плюсы и минусы использования TPM

Плюсы:

  • Аппаратная защита: ключи никогда не покидают TPM в открытом виде.
  • Защита от физических атак: TPM устойчив к попыткам извлечения ключей.
  • Целостность системы: TPM может проверять целостность загрузочного процесса.
  • Изоляция ключей: каждый TPM имеет уникальные ключи, привязанные к оборудованию.
  • Поддержка стандартов: соответствие международным стандартам безопасности.

Минусы:

  • Привязка к оборудованию: ключи не могут быть легко перенесены на другой TPM.
  • Ограниченная производительность: TPM не предназначен для высокоскоростных операций.
  • Сложность управления: требуются специальные инструменты и знания.
  • Ограниченное пространство: ограниченное количество ключей, которые можно хранить.

Анализ векторов атаки на связку PgSodium + TPM

Злоумышленник с доступом к скрипту и файлам системы может попытаться:

  1. Получить первичный ключ с правами PostgreSQL. Если злоумышленник уже имеет привилегии пользователя/процесса, от которого запускается PostgreSQL (например, postgres), он может запустить скрипт сам и получить ключ в момент работы сервера. Меры защиты: разграничение прав, изоляция процесса, контроль целостности.
  2. Украсть дамп памяти процесса PostgreSQL. Можно попытаться сделать дамп памяти процесса и найти ключ там. Меры защиты: защита от эксплуатации уязвимостей в СУБД, использование мандатной защиты (PARSEC/SELinux/AppArmor).
  3. Скомпроментировать защиту операционной системы. Сложная атака, как правило требующая физического доступа. Меры защиты: Secure Boot, trusted boot.
  4. Изменить скрипт так, чтобы он копировал или пересылал ключ при следующей загрузке PostgreSQL. Меры защиты: защита целостности файлов (PARSEC, immutable flags, chattr +i, режим «только для чтения»).
  5. Украсть резервные копии ключа. Резервная копия  ключа, не защищённая TPM (например, для восстановления), его кража — это катастрофа. Меры защиты: хранение резервной копии в сейфе.

Рекомендации по безопасности

  1. Минимизация прав. Скрипт pgsodium_getkey и файл с ciphertext должны быть доступны только на чтение пользователю/группе учётки PostgreSQL (например, postgres:postgres, режим 400 или 440).
  2. Целостность файлов. Установите флаг immutable (chattr +i) на скрипт и файл с ciphertext, чтобы предотвратить их изменение даже root-пользователем (сбросить флаг можно, но это оставит след). Используйте PARSEC в Astra Linux Special Edition.
  3. Изоляция. Запускайте PostgreSQL в отдельном контейнере или виртуальной машине с минимальным набором инструментов.
  4. Аудит. Настройте мониторинг и аудит запуска скрипта pgsodium_getkey и доступа к связанным файлам.
  5. Резервные ключи. Храните резервные копии первичного ключа в сейфе или используйте аппаратные токены.

Заключение

Связка PgSodium и TPM предоставляет надёжную защиту конфиденциальных данных в PostgreSQL, сочетая современные криптографические алгоритмы и аппаратные возможности по безопасности. Настройка такой системы требует значительных усилий и экспертизы, она обеспечивает уровень защиты, необходимый для обработки чувствительных данных в регулируемых отраслях.

0 comments

Что у нас
нового

Блог

Шифрование данных в PostgreSQL ключами в TPM

6 февраля 2026

Шифрование персональных данных с надёжным хранением ключей.

Современная программная инженерия в ВПК

10 сентября 2017

Команда «Лаборатории 50» участвовала в проекте с использованием современных подходов программной инженерии на базе семейства стандартов ISO/IEEE 12207 (в России ГОСТ Р ИСО/МЭК 12207).

Наши
контакты

Связаться с нами

Телефон: 8 (812) 981-68-09
Электронная почта: team@lab50.net






    Заполняя данную форму, вы принимаете условия Соглашения об использовании сайта, и соглашаетесь с Правилами обработки и использования персональных данных