env-master

Безопасность

Что система гарантирует, чего не гарантирует и почему.

Где проходит граница

В покое — только шифртекст. Значения зашифрованы XChaCha20-Poly1305 на data key окружения, сам data key лежит зашифрованным под корневым ключом, который живёт в окружении оператора или во внешнем KMS. Украденный дамп MongoDB без корневого ключа бесполезен.

В работе — зависит от пути:

  • машинный путь честно клиентский: сервер отдаёт SDK шифртекст и data key, обёрнутый x25519 под ключ конкретной identity. Открытого значения сервер в этом сценарии не производит вовсе;
  • показ значения человеку расшифровывается на сервере и отдаётся по TLS.

Поэтому мы не называем систему zero-knowledge. Живой процесс имеет доступ к значениям в момент обработки, и обещать обратное было бы неправдой — именно на такой попытке в своё время споткнулся Infisical, когда дашборду и интеграциям понадобился plaintext.

Аутентификация машин

Ничего вечного: у client secret есть отзыв, у access-токена — TTL в минуты, у enrollment-токена — минуты и ровно одно использование.

Повторный обмен enrollment-токена означает, что он утёк. Система не просто отказывает: захват токена атомарный (compare-and-set), проигравшая попытка поднимает событие enrollment.reuse-detected и вебхук администраторам.

Аудит

Два потока: изменения и доступы. Каждое чтение секрета — человеком или машиной при старте — записано. События связаны хеш-цепочкой на уровне организации, поэтому незаметно удалить или переписать запись нельзя.

Значений в журнале нет: только имена ключей и HMAC значения. Этого хватает, чтобы увидеть «значение изменилось», и не хватает, чтобы узнать какое.

Периметр

  • вход и обмен токенов ограничены по частоте — по IP и по субъекту;
  • адрес вебхука проверяется дважды: при сохранении и перед каждой доставкой, внутренние и loopback-адреса отклоняются, иначе сервер стал бы сканером собственной сети;
  • размер запроса и значения ограничены и в приложении, и на обратном прокси;
  • refresh-токен браузера живёт в httpOnly-cookie, access-токен — только в памяти вкладки: XSS уносит 15 минут, а не 30 дней;
  • CSP, HSTS и запрет фреймов настроены на отдаче статики.

Ротация корневого ключа

Корневой ключ тоже не вечен:

OLD_ROOT_KEY=... NEW_ROOT_KEY=... bun run db:rekey -- --dry-run

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