Gymterview
middle

Как безопасно хранить секреты в приложении?

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

Что НЕЛЬЗЯ делать

Пример
// КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО: секреты в коде
public class DatabaseConfig {
    private static final String DB_PASSWORD = "SuperSecret123!"; // Попадёт в Git
}
Пример
# НЕПРАВИЛЬНО: секреты в application.yml в репозитории
spring:
  datasource:
    password: SuperSecret123!

Уровни хранения секретов (от простого к сложному)

1. Переменные окружения

Простейший способ, подходит для начального уровня:

Пример
# application.yml
spring:
  datasource:
    password: ${DB_PASSWORD}
Пример
export DB_PASSWORD=SuperSecret123!
java -jar app.jar

Минусы: переменные видны через /proc/<pid>/environ, в docker inspect, в логах систем оркестрации.

2. Kubernetes Secrets

Пример
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: payment
type: Opaque
data:
  password: U3VwZXJTZWNyZXQxMjMh  # base64-encoded (НЕ шифрование!)
Пример
# Использование в Pod
spec:
  containers:
    - name: payment-service
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password

Kubernetes Secrets по умолчанию хранятся в etcd в base64 (не зашифровано). Необходимо включить шифрование etcd at rest.

3. HashiCorp Vault

Vault — специализированное решение для управления секретами.

Пример
# Запись секрета
vault kv put secret/payment-service db_password=SuperSecret123!

# Чтение секрета
vault kv get secret/payment-service
Интеграция с Spring Boot через Spring Cloud Vault
# bootstrap.yml
spring:
  cloud:
    vault:
      uri: https://vault.mybank.local:8200
      authentication: KUBERNETES
      kubernetes:
        role: payment-service
      kv:
        backend: secret
        default-context: payment-service
// Секреты автоматически подставляются в Spring-конфигурацию
@Value("${db_password}")
private String dbPassword;

Возможности Vault

  • Динамические секреты — Vault генерирует временные учётные данные к БД, которые автоматически отзываются
  • Ротация секретов — автоматическая смена паролей по расписанию
  • Аудит — полный лог доступа к секретам
  • Шифрование как сервис (Transit engine) — Vault шифрует/расшифровывает данные без раскрытия ключа приложению
Пример динамических секретов для PostgreSQL
vault write database/config/mydb \
    plugin_name=postgresql-database-plugin \
    connection_url="postgresql://{{username}}:{{password}}@db:5432/mydb" \
    allowed_roles="payment-role" \
    username="vault" \
    password="vaultpass"

vault write database/roles/payment-role \
    db_name=mydb \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"

# Получение временных учётных данных
vault read database/creds/payment-role

Рекомендации

Среда Рекомендуемое хранилище
Production HashiCorp Vault, CyberArk, AWS Secrets Manager
Staging/Dev Kubernetes Secrets с шифрованием etcd
Никогда Секреты в Git (даже в приватных репозиториях)

Ротация — регулярная смена всех секретов (минимум раз в квартал). Аудит — логирование всех операций с секретами.

На собеседовании: интервьюер хочет услышать про эволюцию от переменных окружения до Vault и понимание того, что K8s Secrets — это base64, а не шифрование. Частая ошибка — не знать про Vault и динамические секреты.