Gerenciamento de Segredos em Produção: Vault, AWS Secrets Manager e Abordagens Modernas
Segredos hardcoded e variáveis de ambiente são passivos de segurança em ambientes cloud-native. Gerenciamento moderno de segredos fornece credenciais dinâmicas, rotação automática e trilhas de auditoria para sistemas de produção.
Resumo executivo
Segredos hardcoded e variáveis de ambiente são passivos de segurança em ambientes cloud-native. Gerenciamento moderno de segredos fornece credenciais dinâmicas, rotação automática e trilhas de auditoria para sistemas de produção.
Ultima atualizacao: 15/03/2026
Resumo executivo
A vulnerabilidade de segurança mais comum em aplicações cloud-native é o gerenciamento inadequado de segredos. Credenciais hardcoded no código fonte, armazenadas em variáveis de ambiente ou commitadas no controle de versão representam riscos existenciais que não podem ser mitigados através de controles de segurança em nível de aplicação.
Plataformas modernas de gerenciamento de segredos fornecem:
- Credenciais dinâmicas que são de curta duração e rotacionadas automaticamente
- Armazenamento centralizado com criptografia em repouso e em trânsito
- Trilhas de auditoria para cada acesso e modificação de segredo
- Controles de acesso que aplicam menor privilégio
- Rotação automática sem reinicialização de aplicação
Em 2026, gerenciamento de segredos não é opcional para qualquer sistema de produção que manipula dados sensíveis. A escolha é entre usar um gerenciador de segredos dedicado ou aceitar que uma violação revelará credenciais que fornecem acesso completo à sua infraestrutura.
O problema de gerenciamento de segredos
Abordagens tradicionais e suas falhas
| Abordagem | Por que falha em produção |
|---|---|
| Segredos hardcoded | Visíveis em controle de versão, compartilhados entre ambientes, impossível rotacionar |
| Variáveis de ambiente | Visíveis em listagens de processo, armazenadas em config não criptografada, difíceis de rotacionar |
| Arquivos de config no repo | Commitados no controle de versão, acessíveis a qualquer um com acesso ao repo |
| Credenciais de banco compartilhadas | Única violação revela todas credenciais de aplicação, sem isolamento por serviço |
| Rotação manual | Propenso a erro humano, timing inconsistente, frequentemente ignorado até incidente |
O requisito de segurança moderno
Sistemas de produção devem satisfazer estes requisitos:
- Sem credenciais estáticas – Todas credenciais devem ser geradas dinamicamente
- TTL curto – Credenciais expiram automaticamente (minutos a horas, não meses)
- Rotação automática – Sem intervenção manual necessária
- Logging de acesso – Cada acesso a segredo é auditável
- Criptografia – Segredos criptografados em repouso e em trânsito
- Zero knowledge – Desenvolvedores de aplicação nunca devem ver segredos de produção
Plataformas de gerenciamento de segredos
Comparação de plataformas
| Plataforma | Pontos Fortes | Trade-offs |
|---|---|---|
| HashiCorp Vault | Credenciais dinâmicas, extensas integrações, open source | Configuração complexa, overhead operacional |
| AWS Secrets Manager | Integração profunda AWS, rotação automática, serviço gerenciado | Lock-in AWS, tipos limitados de credenciais dinâmicas |
| Azure Key Vault | Integração Azure, validado FIPS 140-2 | Lock-in Azure, curva de aprendizado |
| Google Secret Manager | Integração cloud-native, acesso baseado IAM | Lock-in GCP, menos recursos avançados |
| Infisical | Open source, amigável ao desenvolvedor, scanning de segredos | Plataforma mais nova, ecossistema menor |
Quando usar qual plataforma
Usar HashiCorp Vault quando:
- Você precisa de credenciais dinâmicas para múltiplos tipos de banco de dados
- Você requer advanced secret engines (PKI, Transit, SSH)
- Você quer evitar lock-in de vendor de nuvem
- Você tem requisitos complexos de geração de segredos
Usar AWS Secrets Manager quando:
- Sua infraestrutura é primariamente na AWS
- Você precisa de rotação automática de credenciais de serviço AWS
- Você prefere serviços gerenciados sobre self-hosting
- Você quer integração apertada com AWS IAM
Usar Azure Key Vault quando:
- Sua infraestrutura é primariamente no Azure
- Você precisa de criptografia validada FIPS 140-2
- Você quer integração com Azure AD
Padrões de implementação
Padrão 1: Credenciais dinâmicas de banco de dados
Gerar credenciais de banco de dados de curta duração para cada instância de aplicação:
typescript// Credenciais dinâmicas de banco de dados Vault
import { Client } from 'node-vault';
const vault = new Client({
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN
});
interface CredenciaisBanco {
username: string;
password: string;
leaseId: string;
ttl: number;
}
async function getCredenciaisBancoDinamicas(
nomeBanco: string
): Promise<CredenciaisBanco> {
try {
const resposta = await vault.read({
path: `database/creds/${nomeBanco}`
});
return {
username: resposta.data.username,
password: resposta.data.password,
leaseId: resposta.lease_id,
ttl: resposta.lease_duration
};
} catch (error) {
throw new Error(`Falha ao obter credenciais para ${nomeBanco}: ${error.message}`);
}
}
// Pool de conexão com renovação automática de credenciais
class PoolConexaoBanco {
private credenciais: CredenciaisBanco | null = null;
private pool: Pool;
private timerRenovacao: NodeJS.Timeout | null = null;
constructor(
private nomeBanco: string,
private vault: Client
) {
this.pool = new Pool({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || '5432'),
database: this.nomeBanco,
max: 20,
idleTimeoutMillis: 30000
});
}
async inicializar(): Promise<void> {
await this.refreshCredenciais();
// Agendar renovação antes da expiração
this.timerRenovacao = setInterval(
() => this.renovarCredenciais(),
this.credenciais!.ttl * 1000 * 0.8 // Renovar em 80% do TTL
);
}
private async refreshCredenciais(): Promise<void> {
this.credenciais = await getCredenciaisBancoDinamicas(
this.nomeBanco
);
// Atualizar pool com novas credenciais
this.pool.config.user = this.credenciais.username;
this.pool.config.password = this.credenciais.password;
}
private async renovarCredenciais(): Promise<void> {
try {
await vault.write({
path: `sys/leases/renew`,
data: {
lease_id: this.credenciais!.leaseId,
increment: this.credenciais!.ttl // Renovar por TTL completo
}
});
console.log(`Credenciais renovadas para ${this.nomeBanco}`);
} catch (error) {
console.error(`Falha ao renovar credenciais: ${error.message}`);
await this.refreshCredenciais(); // Obter novas credenciais
}
}
async query<T>(sql: string, params?: any[]): Promise<T[]> {
const cliente = await this.pool.connect();
try {
const resultado = await cliente.query(sql, params);
return resultado.rows;
} finally {
cliente.release();
}
}
async destruir(): Promise<void> {
if (this.timerRenovacao) {
clearInterval(this.timerRenovacao);
}
await this.pool.end();
// Revogar credenciais
if (this.credenciais?.leaseId) {
await vault.write({
path: `sys/leases/revoke`,
data: { lease_id: this.credenciais.leaseId }
});
}
}
}
// Uso
const bancoPedidos = new PoolConexaoBanco('pedidos', vault);
await bancoPedidos.inicializar();
const pedidos = await bancoPedidos.query('SELECT * FROM pedidos LIMIT 10');Padrão 2: AWS Secrets Manager com rotação automática
Usar rotação gerenciada AWS para credenciais de banco e serviço:
typescriptimport { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
const secretsManager = new SecretsManagerClient({});
interface SegredoAWS {
username: string;
password: string;
host: string;
port: number;
dbname: string;
}
async function getSegredoBancoAWS(nomeSegredo: string): Promise<SegredoAWS> {
try {
const comando = new GetSecretValueCommand({
SecretId: nomeSegredo
});
const resposta = await secretsManager.send(comando);
if (!resposta.SecretString) {
throw new Error('SecretString está vazio');
}
const segredo = JSON.parse(resposta.SecretString);
return {
username: segredo.username,
password: segredo.password,
host: segredo.host,
port: segredo.port,
dbname: segredo.dbname
};
} catch (error) {
throw new Error(`Falha ao recuperar segredo ${nomeSegredo}: ${error.message}`);
}
}
// Pool de conexão com cache de segredo e refresh
class PoolConexaoBancoAWS {
private segredo: SegredoAWS | null = null;
private segredoExpira: number = 0;
private pool: Pool;
private readonly CACHE_TTL = 3600000; // 1 hora
constructor(
private nomeSegredo: string
) {
this.pool = new Pool({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || '5432'),
database: this.nomeSegredo,
max: 20
});
}
async getSegredo(): Promise<SegredoAWS> {
const agora = Date.now();
// Retornar segredo em cache se ainda válido
if (this.segredo && agora < this.segredoExpira) {
return this.segredo;
}
// Buscar novo segredo
this.segredo = await getSegredoBancoAWS(this.nomeSegredo);
this.segredoExpira = agora + this.CACHE_TTL;
// Atualizar configuração de pool
this.pool.config.user = this.segredo.username;
this.pool.config.password = this.segredo.password;
this.pool.config.host = this.segredo.host;
this.pool.config.port = this.segredo.port;
this.pool.config.database = this.segredo.dbname;
return this.segredo;
}
async query<T>(sql: string, params?: any[]): Promise<T[]> {
await this.getSegredo(); // Garantir que segredo está atual
const cliente = await this.pool.connect();
try {
const resultado = await cliente.query(sql, params);
return resultado.rows;
} finally {
cliente.release();
}
}
}Padrão 3: Chaves de API com Vault Transit
Gerar e gerenciar chaves de API usando Vault's Transit secrets engine:
typescriptimport { Client } from 'node-vault';
const vault = new Client({
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN
});
class GerenciadorChavesTransit {
constructor(
private nomeChave: string
) {}
async gerarChaveAPI(): Promise<string> {
const resposta = await vault.write({
path: `transit/keys/${this.nomeChave}/export`,
data: {
type: 'encryption-key',
version: 1
}
});
return resposta.data.keys[1]; // Retornar chave de criptografia
}
async criptografarChaveAPI(apiKey: string): Promise<string> {
const resposta = await vault.write({
path: `transit/encrypt/${this.nomeChave}`,
data: {
plaintext: Buffer.from(apiKey).toString('base64'),
context: 'api-key-generation'
}
});
return resposta.data.ciphertext;
}
async descriptografarChaveAPI(ciphertext: string): Promise<string> {
const resposta = await vault.write({
path: `transit/decrypt/${this.nomeChave}`,
data: {
ciphertext,
context: 'api-key-generation'
}
});
return Buffer.from(resposta.data.plaintext, 'base64').toString();
}
}
// Uso
const gerenciadorChaves = new GerenciadorChavesTransit('api-pagamento');
// Gerar e criptografar chave API
const apiKey = gerarChaveAPIAleatoria();
const chaveCriptografada = await gerenciadorChaves.criptografarChaveAPI(apiKey);
// Armazenar chave criptografada no banco
await db.apiKeys.insert({
servico: 'pagamento',
chaveCriptografada
});
// Descriptografar quando necessário
const chaveDescriptografada = await gerenciadorChaves.descriptografarChaveAPI(chaveCriptografada);Padrão 4: Injeção de segredos em runtime
Injetar segredos diretamente em containers de aplicação no startup:
yaml# Deploy Kubernetes com Vault Agent
apiVersion: apps/v1
kind: Deployment
metadata:
name: servico-pedidos
spec:
replicas: 3
selector:
matchLabels:
app: servico-pedidos
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "servico-pedidos"
vault.hashicorp.com/secret-volume-path: "/vault/secrets"
vault.hashicorp.com/secret-volume-mode: "0600"
spec:
serviceAccountName: servico-pedidos
containers:
- name: app
image: servico-pedidos:latest
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: credenciais-banco-pedidos
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: credenciais-banco-pedidos
key: password
volumeMounts:
- name: vault-secrets
mountPath: /vault/secrets
readOnly: true
volumes:
- name: vault-secrets
emptyDir:
medium: MemoryControle de acesso e auditoria
Controle de acesso baseado em função (RBAC)
Definir quem pode acessar quais segredos:
yaml# Policy Vault para serviço de pedidos
path "database/creds/banco-pedidos" {
capabilities = ["read"]
}
path "database/creds/banco-pedidos/*" {
capabilities = ["create", "update", "delete"]
}
path "sys/leases/renew" {
capabilities = ["update"]
}
path "sys/leases/revoke" {
capabilities = ["update"]
}
path "transit/encrypt/api-pagamento" {
capabilities = ["update"]
}
path "transit/decrypt/api-pagamento" {
capabilities = ["update"]
}typescript// Policy AWS IAM para acesso Secrets Manager
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": [
"arn:aws:secretsmanager:us-east-1:123456789012:secret:banco-pedidos-*"
],
"Condition": {
"StringEquals": {
"aws:PrincipalTag/Application": "servico-pedidos"
}
}
},
{
"Effect": "Allow",
"Action": [
"secretsmanager:RotateSecret"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalTag/Role": "admin-segredos"
}
}
}
]
}Logging de auditoria
Rastrear todo acesso e modificação de segredos:
typescriptinterface EventoAcessoSegredo {
timestamp: Date;
nomeSegredo: string;
operacao: 'read' | 'write' | 'delete' | 'renew';
identidadeUsuario: string;
enderecoIP: string;
userAgent: string;
sucesso: boolean;
}
async function registrarAcessoSegredo(evento: EventoAcessoSegredo): Promise<void> {
// Log no banco de auditoria
await db.auditLogs.insert(evento);
// Enviar para SIEM
await siem.send({
severity: evento.sucesso ? 'info' : 'warning',
event_type: 'acesso_segredo',
...evento
});
// Alertar sobre atividade suspeita
if (!evento.sucesso || isAcessoSuspeito(evento)) {
await alerting.notify({
message: 'Acesso suspeito a segredo detectado',
details: evento
});
}
}
function isAcessoSuspeito(evento: EventoAcessoSegredo): boolean {
// Alertar sobre acesso de IP incomum
const acessosRecentes = await db.auditLogs
.where('nomeSegredo', '=', evento.nomeSegredo)
.where('timestamp', '>', new Date(Date.now() - 3600000))
.execute();
const ipsUnicas = new Set(acessosRecentes.map(a => a.enderecoIP));
if (!ipsUnicas.has(evento.enderecoIP)) {
return true; // Novo endereço IP
}
// Alertar sobre frequência excessiva de acesso
const contagemAcesso = acessosRecentes.filter(
a => a.identidadeUsuario === evento.identidadeUsuario
).length;
if (contagemAcesso > 100) {
return true; // Acesso excessivo
}
return false;
}Estratégias de rotação
Estratégia 1: Rotação automática com Vault
Configurar rotação automática para credenciais de banco de dados:
bash# Habilitar database secrets engine
vault secrets enable database
# Configurar conexão PostgreSQL
vault write database/config/banco-pedidos \
plugin_name="postgresql-database-plugin" \
connection_url="postgresql://{{username}}:{{password}}@banco-pedidos:5432/pedidos" \
allowed_roles="servico-pedidos" \
max_ttl="24h"
# Criar role com rotação habilitada
vault write database/roles/servico-pedidos \
db_name="banco-pedidos" \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
default_ttl="1h" \
max_ttl="24h" \
rotation_period="24h"Estratégia 2: Rotação AWS Secrets Manager
Usar rotação gerenciada AWS com Lambda:
typescript// Função Lambda para rotação de segredo
import {
SecretsManagerClient,
RotateSecretCommand,
DescribeSecretCommand
} from '@aws-sdk/client-secrets-manager';
const secretsManager = new SecretsManagerClient({});
export const handler = async (evento: any): Promise<void> => {
const idSegredo = evento.SecretId;
// Passo 1: Criar nova versão de segredo
const novasCredenciais = await criarNovasCredenciais(idSegredo);
// Passo 2: Definir nova versão de segredo
await secretsManager.send(new RotateSecretCommand({
SecretId: idSegredo,
ClientRequestToken: evento.ClientRequestToken,
RotationStage: 'CREATING'
}));
// Passo 3: Testar novas credenciais
await testarCredenciais(novasCredenciais);
// Passo 4: Marcar nova versão como atual
await secretsManager.send(new RotateSecretCommand({
SecretId: idSegredo,
ClientRequestToken: evento.ClientRequestToken,
RotationStage: 'AWSCURRENT'
}));
console.log(`Segredo ${idSegredo} rotacionado com sucesso`);
};Melhores práticas
1. Nunca logar segredos
typescript// Ruim: Logando segredos
console.log(`Conectando ao banco com ${username}:${password}`);
// Bom: Logar apenas operação
console.log(`Conectando ao banco`);2. Usar nomenclatura de segredo específica de ambiente
production/credenciais-banco-pedidos
staging/credenciais-banco-pedidos
development/credenciais-banco-pedidos3. Implementar validação de segredos
typescriptasync function validarSegredo(segredo: SegredoAWS): Promise<boolean> {
const cliente = new Client({
host: segredo.host,
port: segredo.port,
user: segredo.username,
password: segredo.password,
database: segredo.dbname,
connectTimeoutMS: 5000
});
try {
await cliente.connect();
await cliente.end();
return true;
} catch (error) {
console.error(`Validação de segredo falhou: ${error.message}`);
return false;
}
}4. Usar TTLs curtos
- Credenciais de banco: 1-24 horas
- Chaves de API: 1-6 horas
- Tokens de serviço: 15-30 minutos
Conclusão
Gerenciamento de segredos em 2026 é uma disciplina madura com múltiplas soluções prontas para produção. HashiCorp Vault fornece o conjunto de recursos mais abrangente para ambientes complexos, enquanto opções cloud-native como AWS Secrets Manager oferecem implementação mais simples para infraestruturas centradas em AWS.
A organização madura trata segredos como componentes de infraestrutura de primeira classe com rotação automática, gerenciamento centralizado e trilhas de auditoria abrangentes. Implementar gerenciamento adequado de segredos não é opcional—é uma linha de base de segurança para qualquer sistema de produção.
Precisa implementar gerenciamento de segredos ou auditar suas práticas atuais de tratamento de segredos? Fale com a Imperialis sobre arquitetura de gerenciamento de segredos, seleção de plataforma e implementação em produção.
Fontes
- Documentação HashiCorp Vault — Documentação oficial
- Documentação AWS Secrets Manager — Documentação AWS
- Documentação Azure Key Vault — Documentação Azure
- Documentação Google Secret Manager — Documentação GCP
- NIST SP 800-57: Key Management — Gerenciamento de chaves criptográficas