Безпечне управління секретами в Next.js і Node.js з HashiCorp Vault

Крок за кроком покажемо, як створити надійний процес управління секретами в застосунках Next.js і Node.js за допомогою HashiCorp Vault.

Bezpieczne zarządzanie sekretami w Next.js i Node.js z HashiCorp Vault

Сучасні веб‑додатки, побудовані на Next.js та Node.js, обробляють все більше конфіденційних даних – API‑ключі, JWT‑токени, облікові дані баз даних. Неправильне зберігання цих секретів призводить до витоків, які можуть коштувати організації мільйони гривень. У статті ми представляємо повну модель загрози, описуємо механізм роботи HashiCorp Vault та демонструємо практичні фрагменти коду – від вразливого до виправленого – а також чек‑лист мітигації, заснований на OWASP, CWE та принципі найменших привілеїв.

Модель загрози – де з’являється ризик?

Типовий потік секрету в проєкті Next.js/Node.js виглядає так: розробник записує значення у файл .env, CI/CD‑репозиторій завантажує файл і інжектує його в контейнер Docker, а додаток читає змінну середовища під час запуску. У цій моделі існують три критичні точки атаки:

  • зберігання секрету в репозиторії (навіть у приватному репозиторії його можна клонувати або витікнути);
  • передача секрету як незашифрований рядок у змінних середовища в контейнері;
  • відсутність обмежень доступу додатка до конкретного секрету (відсутність принципу найменших привілеїв).

Усі ці сценарії описані в OWASP Top 10 як Security Misconfiguration (A05) та в CWE‑522 – Insufficiently Protected Credentials. Найкращий спосіб зменшити ризик – це централізоване управління секретами, яке забезпечує аудит, ротацію та мінімальний обсяг прав.

HashiCorp Vault – як це працює?

Vault – це система управління таємницями, яка зберігає зашифровані значення в безпечному бекенді (наприклад, у консенсусному сховищі). Доступ до секретів отримується після автентифікації за допомогою методів, таких як AppRole, Kubernetes Auth або TLS‑сертифікати. Після успішної автентифікації Vault видає короткоживучий токен (TTL від кількох секунд до кількох годин) і дозволяє читати або генерувати динамічні облікові дані (наприклад, тимчасових користувачів бази).

Ключові особливості, які відповідають вимогам OWASP і CWE:

  • encryption‑at‑rest і encryption‑in‑transit (TLS);
  • строга контроль політик (policy‑as‑code), що визначає, які шляхи та операції дозволені;
  • автоматична ротація та анулювання токенів.

Інтеграція HashiCorp Vault з Node.js – приклад коду

Нижче наведено простий, але вразливий фрагмент коду, у якому секрети завантажуються з файлу .env і використовуються безпосередньо в запитах до API:

require('dotenv').config();
const axios = require('axios');

const apiKey = process.env.API_KEY; // вразливий: ключ у .env

async function callExternal() {
  const resp = await axios.get('https://api.example.com/data', {
    headers: { 'Authorization': `Bearer ${apiKey}` }
  });
  return resp.data;
}

Проблема в тому, що API_KEY може потрапити до логів CI, до образу Docker або в пам’ять контейнера, і немає контролю, які процеси можуть його прочитати.

Виправлена версія використовує Vault SDK, автентифікується за допомогою AppRole і отримує секрет у реальному часі. Завдяки цьому токен має короткий TTL, а ключ не зберігається в пам’яті довше, ніж це необхідно:

const vault = require('node-vault')({
  endpoint: process.env.VAULT_ADDR,
  token: process.env.VAULT_APPROLE_TOKEN // короткий токен, не постійний
});

async function getApiKey() {
  const result = await vault.read('secret/data/app/api-key');
  return result.data.data.key;
}

async function callExternal() {
  const apiKey = await getApiKey();
  const resp = await axios.get('https://api.example.com/data', {
    headers: { 'Authorization': `Bearer ${apiKey}` }
  });
  return resp.data;
}

Варто зазначити, що Vault SDK автоматично обробляє оновлення токену, а політика app/api-key може обмежити доступ лише до читання цього конкретного ключа.

Docker і Kubernetes – безпечне зберігання JWT‑токенів

У контейнерних середовищах найчастіше використовують змінні середовища для передачі токенів. Замість цього можна застосувати:

  • Vault Agent Injector – автоматично інжектує секрет як файл у /vault/secrets в контейнері;
  • Sidecar‑контейнер з Vault Agent, який оновлює токен у фоні.

Приклад манифесту Kubernetes з sidecar:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nextjs-app
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: app
          image: myregistry/nextjs:latest
          env:
            - name: VAULT_ADDR
              value: "https://vault.mycompany.com"
          volumeMounts:
            - name: vault-secrets
              mountPath: /vault/secrets
        - name: vault-agent
          image: hashicorp/vault-agent:latest
          args:
            - "agent"
            - "-config=/etc/vault/agent-config.hcl"
          volumeMounts:
            - name: vault-config
              mountPath: /etc/vault
            - name: vault-secrets
              mountPath: /vault/secrets
      volumes:
        - name: vault-config
          configMap:
            name: vault-agent-config
        - name: vault-secrets
          emptyDir: {}

Sidecar записує оновлений токен у /vault/secrets/jwt, а застосунок читає його під час запуску. Завдяки цьому токен не з’являється у змінних середовища і автоматично анулюється після закінчення терміну дії.

Чек‑лист мітигації – від OWASP до принципу найменших привілеїв

  • Використовуйте центральне сховище (Vault) замість файлів .env у репозиторії.
  • Застосовуйте короткі TTL токенів і автоматичну ротацію.
  • Визначайте політики Vault у вигляді path "secret/data/app/*" { capabilities = ["read"] } – обмежуйте доступ до конкретного простору імен.
  • У Docker та Kubernetes ін’єкціюйте секрети як файли або volume, а не як змінні середовища.
  • Аудитуйте доступ – увімкніть audit device у Vault і інтегруйте логи з SIEM.
  • Перевіряйте права застосунку (least‑privilege) – застосунок має мати доступ лише до необхідних секретів.

Типові помилки та компроміси

Even experienced teams popełniają pewne pułapki:

  • Hard‑coding tokenów – розміщення постійних токенів у вихідному коді під приводом швидкого прототипу; рішення: завжди використовуйте AppRole або Kubernetes Auth.
  • Ustawianie zbyt szerokich polityk – напр. path "secret/*" { capabilities = ["read"] } дає доступ до всіх секретів; рішення: розділіть секрети за доменами і надайте мінімальні права.
  • Brak rotacji – залишення незмінних API‑ключів на роки; рішення: налаштуйте max_ttl і rotation_period у Vault.
  • Logowanie sekretów – несвідоме виведення значень у логи застосунку; рішення: використовуйте логер, який маскує поля з іменами *secret*, *key*, *token*.
Безпека – це не одноразова конфігурація, а постійний процес контролю, аудиту та обмеження привілеїв.

Практичний приклад – повний workflow

1. Konfiguracja Vault: створіть namespace app, увімкніть audit device, визначте політику app-read з доступом до secret/data/app/*.

2. Utworzenie AppRole: role_id і secret_id зберігаються у Kubernetes Secret (не в репозиторії).

3. Deploy aplikacji: манифест з sidecar Vault Agent, як у наведеному вище прикладі.

4. W aplikacji Next.js (pages/api/data.ts):

import { createClient } from '@next-auth/vault';

export default async function handler(req, res) {
  const client = createClient({
    endpoint: process.env.VAULT_ADDR,
    roleId: process.env.VAULT_ROLE_ID,
    secretId: process.env.VAULT_SECRET_ID
  });
  const { data } = await client.read('secret/data/app/jwt');
  const jwt = data.data.token;
  // użycie tokenu do autoryzacji zewnętrznego serwisu
  res.status(200).json({ status: 'ok' });
}

5. Testy bezpieczeństwa: запустіть скан OWASP ZAP, перевірте, чи немає витоків змінних середовища у логах контейнера.

Після впровадження цього workflow команда інженерів отримує:

  • центральне місце для управління всіма секретами;
  • можливість миттєвої ротації у разі інциденту;
  • повний аудиторський контроль, відповідний OWASP і CWE.

Якщо ви хочете, щоб ваші проекти Next.js і Node.js були стійкими до витоків даних, зв’яжіться з Coderia.it – ми допоможемо впровадити Vault, налаштувати політики та провести аудит безпеки.

Почнімо

Маєте проєкт на думці?

Опишіть його кількома реченнями. Відповім протягом 24 годин із безкоштовною оцінкою та пропозицією стеку.