Por que usar o Secrets Manager em vez de hardcoding de credenciais?

A pergunta aparece toda semana em revisões de código: 'o repositório é privado, então qual é o problema de deixar a senha do banco no application.properties?' O problema é que repositório privado não é modelo de segurança — é uma suposição que falha no primeiro acidente de permissão, no primeiro desenvolvedor que sai da empresa, ou no primeiro vazamento de token do GitHub. O AWS Secrets Manager existe exatamente para eliminar essa suposição do design da aplicação.

TL;DR — Hardcoding vs. Secrets Manager

CritérioHardcoding / Variável de AmbienteAWS Secrets Manager
Rotação de senhaManual, requer redeployAutomática via Lambda rotation function
Auditoria de acessoNenhumaCloudTrail registra cada GetSecretValue
Superfície de exposiçãoCódigo-fonte, logs, variáveis de processoApenas em memória, no momento do uso
Controle de acessoQuem tem acesso ao repo tem a senhaIAM policy por role, por recurso
Vazamento em histórico gitPermanente mesmo após remoçãoSem credencial no repositório
Custo operacionalZero direto, alto em incidentesPricing por segredo/mês — verificar documentação oficial

Como o Secrets Manager funciona — antes de agir, entenda o mecanismo

O Secrets Manager armazena segredos como pares chave-valor em JSON, criptografados com uma chave KMS. Quando a aplicação chama GetSecretValue, o serviço descriptografa o valor em memória e retorna via HTTPS. A credencial nunca trafega em texto plano na rede AWS.

A rotação automática funciona por meio de uma Lambda function que o próprio Secrets Manager invoca em um agendamento configurável. Essa função executa quatro etapas distintas — createSecret, setSecret, testSecret, finishSecret — e o serviço orquestra essas etapas. Para bancos de dados RDS, AWS fornece funções de rotação prontas. Para outros sistemas, você implementa a função seguindo o contrato documentado.

graph LR App["Aplicação (ECS Task / EC2)"] -->|"GetSecretValue via HTTPS"| SM["AWS Secrets Manager"] SM -->|"Decrypt"| KMS["AWS KMS"] KMS -->|"Valor descriptografado"| SM SM -->|"SecretString em memória"| App App -->|"Conecta com credencial"| RDS["Amazon RDS"] Scheduler["EventBridge Scheduler"] -->|"Dispara rotação"| SM SM -->|"Invoca"| Lambda["Lambda Rotation Function"] Lambda -->|"Atualiza senha"| RDS Lambda -->|"Promove nova versão"| SM CloudTrail["AWS CloudTrail"] -.->|"Registra GetSecretValue"| SM
  1. Aplicação chama GetSecretValue — a chamada é autenticada via IAM role da instância/task, sem credencial estática.
  2. Secrets Manager descriptografa via KMS — a chave KMS nunca sai do serviço; apenas o valor descriptografado é retornado.
  3. Rotação agendada dispara a Lambda — o Secrets Manager invoca a função com o token de rotação e aguarda cada etapa.
  4. Nova versão do segredo é promovida — a versão anterior recebe o label AWSPREVIOUS; a nova recebe AWSCURRENT. Aplicações que buscam AWSCURRENT recebem a nova credencial automaticamente.

Por que 'repositório privado' não é argumento de segurança

Esse é o ponto onde a maioria dos times se engana. A premissa 'o repo é privado' colapsa em pelo menos cinco cenários reais:

  • Histórico git é permanente. Um git log ou git show expõe commits antigos mesmo depois de remover o arquivo. Ferramentas como truffleHog e o próprio GitHub secret scanning varrem o histórico completo.
  • Acesso ao repo ≠ acesso controlado ao banco. Qualquer desenvolvedor com clone local tem a senha de produção. Não há granularidade.
  • Logs de aplicação frequentemente imprimem variáveis de ambiente. Frameworks de diagnóstico, dumps de contexto em exceções, e configurações de debug fazem isso silenciosamente.
  • Tokens de CI/CD têm escopo amplo. Um pipeline comprometido que tem acesso ao repositório tem acesso à credencial.
  • Rotação é operacionalmente impossível sem downtime. Trocar a senha exige atualizar o código ou a variável de ambiente e fazer redeploy — o que significa que a rotação raramente acontece na prática.
Repositório privado é controle de acesso ao código. Não é controle de acesso à credencial. São camadas diferentes com superfícies de ataque diferentes.

Criando e acessando um segredo — passo a passo operacional

Passo 1 — Criar o segredo no Secrets Manager

Antes de qualquer coisa, o segredo precisa existir. O comando abaixo cria um segredo com as credenciais do banco em formato JSON, que é o formato esperado pelas funções de rotação nativas do RDS.

aws secretsmanager create-secret \
  --name prod/myapp/db-credentials \
  --description 'Credenciais do banco de dados de producao' \
  --secret-string '{"username":"appuser","password":"SenhaInicialForte123!"}' \
  --region us-east-1

O nome do segredo funciona como um path lógico. Usar prefixos como prod/, staging/ facilita o controle de acesso via IAM condition keys — você pode restringir roles de produção apenas a segredos com o prefixo prod/.

Passo 2 — Configurar a IAM policy para a aplicação

A aplicação precisa de permissão explícita para ler o segredo. O princípio do menor privilégio aqui significa: acesso apenas ao segredo específico, apenas para leitura, apenas para a role que executa a aplicação. Nenhuma permissão de escrita ou listagem é necessária para o caso de uso de leitura.

🔽 Clique para expandir — IAM Policy para leitura do segredo
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LerSegredoBancoDados",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/db-credentials-*"
    },
    {
      "Sid": "DescriptografarViaKMS",
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt"
      ],
      "Resource": "arn:aws:kms:us-east-1:123456789012:key/SUA-CHAVE-KMS-ID"
    }
  ]
}

O sufixo -* no ARN do segredo é necessário porque o Secrets Manager acrescenta um sufixo aleatório de 6 caracteres ao nome do segredo no ARN real. Sem esse wildcard, a policy bloqueia o acesso mesmo com o nome correto.

Passo 3 — Verificar o segredo criado e sua versão atual

Confirme que o segredo existe e que a versão AWSCURRENT está disponível antes de configurar a rotação. Esse passo evita depurar a rotação quando o problema é simplesmente que o segredo não foi criado corretamente.

aws secretsmanager describe-secret \
  --secret-id prod/myapp/db-credentials \
  --region us-east-1
aws secretsmanager get-secret-value \
  --secret-id prod/myapp/db-credentials \
  --version-stage AWSCURRENT \
  --region us-east-1

Passo 4 — Configurar rotação automática para RDS

Para instâncias RDS, o Secrets Manager oferece funções de rotação gerenciadas pela AWS. Você referencia o ARN da função Lambda de rotação correspondente ao seu engine de banco de dados — os ARNs dessas funções estão documentados na página oficial do Secrets Manager para rotação de RDS. O comando abaixo habilita rotação a cada 30 dias:

aws secretsmanager rotate-secret \
  --secret-id prod/myapp/db-credentials \
  --rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSMySQLRotationSingleUser \
  --rotation-rules AutomaticallyAfterDays=30 \
  --region us-east-1

O parâmetro --rotation-lambda-arn deve apontar para a função de rotação correta para o seu engine. Consulte a documentação oficial para obter o ARN exato da função gerenciada pela AWS para o seu caso — os ARNs variam por região e por tipo de banco de dados.

graph TD Start["Rotação Agendada Secrets Manager"] --> A A["createSecret Gera nova senha versão AWSPENDING"] --> B B["setSecret Aplica nova senha no banco RDS"] --> C C["testSecret Valida conexão com nova credencial"] --> D D{"Teste OK?"} D -->|"Sim"| E["finishSecret Promove AWSPENDING para AWSCURRENT"] D -->|"Não"| F["Rotação falha AWSCURRENT mantido Evento no EventBridge"] E --> G["Versão anterior recebe label AWSPREVIOUS"] F --> H["Alerta CloudWatch Rotation falhou"]
  1. createSecret — a função cria uma nova versão do segredo com label AWSPENDING, gerando uma nova senha.
  2. setSecret — a função aplica a nova senha no banco de dados RDS.
  3. testSecret — a função testa a conexão com a nova credencial para confirmar que funciona.
  4. finishSecret — o Secrets Manager promove AWSPENDING para AWSCURRENT e rebaixa a versão anterior para AWSPREVIOUS.

Passo 5 — Verificar o histórico de rotação e auditoria via CloudTrail

Após a rotação, confirme que a nova versão foi promovida. O CloudTrail registra cada chamada GetSecretValue com o ARN do principal que fez a chamada — isso é o que torna o Secrets Manager auditável de forma que variáveis de ambiente nunca serão.

aws secretsmanager list-secret-version-ids \
  --secret-id prod/myapp/db-credentials \
  --region us-east-1
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue \
  --region us-east-1

O erro que todo time comete na primeira rotação — experiência de campo

O sintoma: a rotação executa com sucesso (o console mostra 'Last rotated' com timestamp recente), mas a aplicação começa a retornar erros de autenticação no banco de dados minutos depois.

O diagnóstico errado: time assume que a rotação falhou e reverte manualmente a senha no RDS, quebrando o segredo no Secrets Manager.

A causa real: a aplicação estava fazendo cache da conexão com o banco e não estava relendo o segredo após a rotação. O pool de conexões mantinha as conexões abertas com a senha antiga. Quando o RDS invalidou a senha antiga, as conexões existentes começaram a falhar — mas a aplicação não tentava buscar o novo segredo porque não havia lógica de retry com releitura do Secrets Manager.

O fix correto tem duas partes: implementar lógica de retry na aplicação que, ao receber erro de autenticação, chama GetSecretValue novamente antes de desistir; e configurar o pool de conexões para validar conexões antes de reutilizá-las. A rotação do Secrets Manager funciona corretamente — o problema estava na camada de aplicação que assumia que a credencial era estática.

Rotação automática de senha só funciona de ponta a ponta se a aplicação foi projetada para tratar credenciais como temporárias — não como constantes.

Integração com aplicações — padrão recomendado em Java e Python

A aplicação nunca deve armazenar o valor do segredo em uma variável de longa duração. O padrão correto é buscar o segredo no momento da inicialização da conexão e implementar retry com releitura em caso de falha de autenticação.

🔽 Clique para expandir — Exemplo Python com boto3
import boto3
import json
from botocore.exceptions import ClientError

def get_db_credentials(secret_name: str, region_name: str) -> dict:
    client = boto3.client('secretsmanager', region_name=region_name)
    try:
        response = client.get_secret_value(SecretId=secret_name)
    except ClientError as e:
        raise e
    secret = json.loads(response['SecretString'])
    return secret

# Uso:
credentials = get_db_credentials('prod/myapp/db-credentials', 'us-east-1')
username = credentials['username']
password = credentials['password']

Em produção, adicione lógica de retry: se a conexão com o banco falhar com erro de autenticação, chame get_db_credentials novamente antes de propagar o erro. Isso garante que a aplicação se recupera automaticamente após uma rotação sem necessidade de redeploy.

Secrets Manager vs. Parameter Store — quando usar cada um

Essa dúvida aparece sempre. A distinção prática: use o Secrets Manager quando a rotação automática é necessária ou quando o dado é uma credencial de acesso a sistema externo. Use o SSM Parameter Store (com SecureString) para configurações sensíveis que não rotacionam — como feature flags criptografadas, ARNs de recursos, ou configurações de ambiente.

O Secrets Manager tem custo por segredo armazenado e por chamada de API acima do free tier. Para configurações que são lidas milhares de vezes por segundo e não precisam de rotação, o Parameter Store é mais econômico. Sempre verifique a tabela de preços atual na documentação oficial antes de decidir a arquitetura.

Por que adotar o Secrets Manager é a decisão certa para produção

Hardcoding de credenciais não é um atalho técnico — é uma dívida de segurança com juros compostos. Cada desenvolvedor que entra no time recebe acesso implícito a credenciais de produção. Cada pipeline de CI/CD que clona o repositório carrega a credencial. Cada dump de variáveis de ambiente em um log de erro expõe o segredo.

O Secrets Manager resolve o problema na camada correta: a credencial existe apenas no serviço gerenciado, é acessada apenas por roles com permissão explícita, cada acesso é auditado, e a rotação acontece sem intervenção humana. O custo operacional de configurar corretamente na primeira vez é menor do que o custo de responder a um incidente de vazamento de credencial.

Para times que estão migrando de hardcoding para Secrets Manager, o caminho mais seguro é: criar o segredo, atualizar a aplicação para ler via SDK, validar em staging, e só então remover a credencial do código — nunca na ordem inversa.

Próximos passos e recursos

  • Consulte a documentação oficial do AWS Secrets Manager para detalhes sobre funções de rotação por engine de banco de dados.
  • Revise o guia de rotação automática para entender o contrato da Lambda function de rotação.
  • Configure alertas no CloudWatch para a métrica de falhas de rotação — o Secrets Manager publica eventos no EventBridge quando uma rotação falha.
  • Avalie o uso de VPC Endpoints para o Secrets Manager se suas aplicações rodam em subnets privadas sem NAT Gateway — isso elimina o tráfego de saída para a internet pública.

Glossário de termos-chave

TermoDefinição operacional
Secret versionCada valor armazenado no Secrets Manager recebe uma versão com labels como AWSCURRENT e AWSPREVIOUS. A rotação cria uma nova versão sem deletar a anterior imediatamente.
Rotation functionLambda function que implementa o protocolo de rotação de quatro etapas do Secrets Manager. Para RDS, AWS fornece funções prontas; para outros sistemas, você implementa seguindo o contrato documentado.
KMS CMKCustomer Managed Key — chave KMS usada para criptografar o segredo. Se não especificada, o Secrets Manager usa a chave gerenciada pela AWS para o serviço.
AWSCURRENT / AWSPREVIOUSLabels de versão do segredo. Aplicações que buscam sem especificar versão recebem AWSCURRENT. AWSPREVIOUS permite rollback manual se necessário.
Resource-based policyPolicy anexada diretamente ao segredo (não à role) que controla quais principals podem acessá-lo. Útil para acesso cross-account.

Related Posts

Comentários