Как безопасно хранить секреты в приложении?
Секреты (пароли, ключи 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 и динамические секреты.