Cloud e plataforma

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.

15/03/20268 min de leituraCloud
Gerenciamento de Segredos em Produção: Vault, AWS Secrets Manager e Abordagens Modernas

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

AbordagemPor que falha em produção
Segredos hardcodedVisíveis em controle de versão, compartilhados entre ambientes, impossível rotacionar
Variáveis de ambienteVisíveis em listagens de processo, armazenadas em config não criptografada, difíceis de rotacionar
Arquivos de config no repoCommitados 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 manualPropenso a erro humano, timing inconsistente, frequentemente ignorado até incidente

O requisito de segurança moderno

Sistemas de produção devem satisfazer estes requisitos:

  1. Sem credenciais estáticas – Todas credenciais devem ser geradas dinamicamente
  2. TTL curto – Credenciais expiram automaticamente (minutos a horas, não meses)
  3. Rotação automática – Sem intervenção manual necessária
  4. Logging de acesso – Cada acesso a segredo é auditável
  5. Criptografia – Segredos criptografados em repouso e em trânsito
  6. Zero knowledge – Desenvolvedores de aplicação nunca devem ver segredos de produção

Plataformas de gerenciamento de segredos

Comparação de plataformas

PlataformaPontos FortesTrade-offs
HashiCorp VaultCredenciais dinâmicas, extensas integrações, open sourceConfiguração complexa, overhead operacional
AWS Secrets ManagerIntegração profunda AWS, rotação automática, serviço gerenciadoLock-in AWS, tipos limitados de credenciais dinâmicas
Azure Key VaultIntegração Azure, validado FIPS 140-2Lock-in Azure, curva de aprendizado
Google Secret ManagerIntegração cloud-native, acesso baseado IAMLock-in GCP, menos recursos avançados
InfisicalOpen source, amigável ao desenvolvedor, scanning de segredosPlataforma 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: Memory

Controle 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-pedidos

3. 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

Leituras relacionadas