Приложение шифрует чувствительные поля (номера документов, телефоны, токены) через API Stronghold и сохраняет в базе данных только шифртекст. Ключ шифрования не покидает Stronghold, поэтому утечка дампа базы данных не раскрывает данные.
Цель
Настроить ключ Transit для приложения, зашифровать и расшифровать поле, применить конвертное шифрование для больших объектов, выполнить ротацию ключа, перешифровать данные (rewrap) и запретить расшифровку старыми версиями ключа.
Предварительные требования
Для выполнения примера нужны:
- Токен Stronghold с правами на включение механизмов секретов и создание политик.
- Утилиты
base64,jq,opensslиxxdдля примеров.
Шаг 1. Создайте ключ
Включите механизм секретов Transit и создайте ключ:
Включите механизм секретов:
d8 stronghold secrets enable transitСоздайте именованный ключ для приложения:
d8 stronghold write -f transit/keys/orders type=aes256-gcm96Используйте отдельный ключ для каждого приложения или набора данных.
Шаг 2. Разделите права
Создайте отдельные политики для приложения и для задания перешифрования:
Политика приложения — только шифрование и расшифровка:
d8 stronghold policy write orders-app - <<'POLICY' path "transit/encrypt/orders" { capabilities = ["update"] } path "transit/decrypt/orders" { capabilities = ["update"] } path "transit/datakey/plaintext/orders" { capabilities = ["update"] } POLICYПолитика задачи перешифрования — только rewrap, без доступа к открытым данным:
d8 stronghold policy write orders-rewrap - <<'POLICY' path "transit/rewrap/orders" { capabilities = ["update"] } path "transit/keys/orders" { capabilities = ["read"] } POLICY
Управление ключом (transit/keys/orders/*) оставьте администраторам.
Шаг 3. Зашифруйте и расшифруйте поле
Данные передаются в Stronghold в кодировке base64.
Зашифруйте значение:
d8 stronghold write -field=ciphertext transit/encrypt/orders plaintext=$(echo -n "4509 123456" | base64)Результат вида
vault:v1:...сохраните в столбце таблицы. Префиксv1— версия ключа, которой выполнено шифрование.Расшифруйте значение:
d8 stronghold write -field=plaintext transit/decrypt/orders ciphertext="vault:v1:..." | base64 --decode
Для обработки множества записей за один запрос используйте параметр batch_input эндпоинтов encrypt, decrypt и rewrap.
Шаг 4. Используйте конвертное шифрование для больших данных
Передавать через API файлы и большие объекты неэффективно. Вместо этого запросите ключ данных (data key): Stronghold возвращает его в открытом виде для локального шифрования и в зашифрованном виде для хранения.
Получите ключ данных:
d8 stronghold write -format=json -f transit/datakey/plaintext/orders > datakey.json jq -r '.data.plaintext' datakey.json | base64 --decode > dek.bin jq -r '.data.ciphertext' datakey.json > dek.encЗашифруйте файл локально и удалите открытый ключ:
openssl rand -hex 16 > report.pdf.iv openssl enc -aes-256-cbc -K "$(xxd -p -c 64 dek.bin)" -iv "$(cat report.pdf.iv)" -in report.pdf -out report.pdf.enc shred -u dek.binКлюч данных имеет длину 256 бит и передаётся в
opensslкак AES-ключ в шестнадцатеричном виде через параметр-K. Файлreport.pdf.ivсодержит случайный вектор инициализации (IV). Генерируйте новый IV для каждого файла. Сохранитеreport.pdf.encвместе сdek.encиreport.pdf.iv.Для расшифровки восстановите ключ данных через Transit и расшифруйте файл с тем же IV:
d8 stronghold write -field=plaintext transit/decrypt/orders ciphertext="$(cat dek.enc)" | base64 --decode > dek.bin openssl enc -d -aes-256-cbc -K "$(xxd -p -c 64 dek.bin)" -iv "$(cat report.pdf.iv)" -in report.pdf.enc -out report.pdf shred -u dek.bin
Если ключ данных нужен только для хранения (например, его расшифрует другой сервис), используйте transit/datakey/wrapped/orders — ответ не содержит открытого ключа.
Шаг 5. Выполните ротацию ключа
Выполните ротацию ключа вручную или настройте автоматическую ротацию:
Создайте новую версию ключа:
d8 stronghold write -f transit/keys/orders/rotateНовые данные шифруются последней версией, старые по-прежнему расшифровываются.
Чтобы ротация выполнялась автоматически, задайте период:
d8 stronghold write transit/keys/orders/config auto_rotate_period=720h
Шаг 6. Перешифруйте данные (rewrap)
Rewrap перешифровывает шифртекст последней версией ключа без раскрытия открытых данных. Выполните его для всех записей таблицы, например фоновой задачей с политикой orders-rewrap:
d8 stronghold write -field=ciphertext transit/rewrap/orders ciphertext="vault:v1:..."Результат vault:v2:... запишите обратно в столбец.
Шаг 7. Запретите расшифровку старыми версиями
Когда в базе данных не осталось шифртекста со старыми версиями, поднимите минимальную версию для расшифровки:
d8 stronghold write transit/keys/orders/config min_decryption_version=2Версии ниже min_decryption_version переносятся в архив и не используются для расшифровки. В экстренном случае значение можно вернуть назад.
Если старые версии нужно удалить окончательно, используйте transit/keys/orders/trim с параметром min_available_version. Это действие необратимо.
Проверка
Проверьте результат:
Просмотрите состояние ключа:
d8 stronghold read transit/keys/ordersПроверьте значения
latest_version,min_decryption_versionиauto_rotate_period.Попробуйте расшифровать шифртекст
vault:v1:...после шага 7 — запрос должен завершиться ошибкой.Убедитесь, что токен с политикой
orders-rewrapне может выполнитьtransit/decrypt/orders.
Очистка
Удалите объекты, созданные в примере:
Удалите политики:
d8 stronghold policy delete orders-appиd8 stronghold policy delete orders-rewrap.Удалите локальные файлы
datakey.json,dek.enc,report.pdf.iv,report.pdf.enc.Для удаления тестового ключа разрешите удаление и удалите ключ:
d8 stronghold write transit/keys/orders/config deletion_allowed=true d8 stronghold delete transit/keys/ordersДанные, зашифрованные этим ключом, станут недоступны.