1. O problema: privilégio excessivo é a norma, não a exceção

Nos dois artigos anteriores desta série, vimos como evitar que segredos vazem em texto puro e como bloquear recursos inseguros antes do apply com Policy as Code. Fechamos a série com um problema mais silencioso, porque não gera erro nem alerta: permissões de IAM concedidas com mais alcance do que o necessário. Uma role de aplicação com s3:* quando só precisa de s3:GetObject em um bucket específico. Um usuário de CI com AdministratorAccess porque "assim resolve na hora e ninguém precisa pedir mais permissão depois". Nada disso quebra nada — até o dia em que uma credencial é comprometida, e o raio de ação do atacante é exatamente do tamanho da permissão que ninguém restringiu.

2. Erros comuns de permissão excessiva

  • Actions com wildcard: "Action": "s3:*" ou "Action": "*" em vez de listar exatamente as operações necessárias (s3:GetObject, s3:PutObject).
  • Resources com wildcard: "Resource": "*" quando a intenção real era restringir a um bucket ou prefixo específico.
  • Roles compartilhadas entre serviços diferentes: uma única role "backend-role" usada por várias Lambdas/serviços com necessidades distintas, acumulando permissões de todos ao longo do tempo porque é mais fácil reaproveitar do que criar uma nova.
  • Credenciais de longa duração: access keys de usuário IAM sem rotação, em vez de roles assumidas temporariamente (via sts:AssumeRole) ou identidades federadas (OIDC em pipelines de CI).
  • Permissões concedidas e nunca revisadas: um acesso liberado para resolver um incidente pontual ("dá acesso de admin no S3 pra debugar isso rápido") que nunca é removido depois.

3. Desenhando políticas mínimas: metodologia

Least privilege na prática raramente é "acertar de primeira" — é um processo iterativo:

  1. Comece restritivo, não permissivo. Ao criar uma role nova, liste apenas as actions que o código efetivamente chama, mesmo que isso signifique testar e ajustar algumas vezes até funcionar.
  2. Use os logs de acesso reais para calibrar. Ferramentas como o IAM Access Analyzer (AWS) geram recomendações de política baseadas no uso real registrado em CloudTrail — em vez de adivinhar quais actions são necessárias, você vê quais foram efetivamente chamadas em um período.
  3. Restrinja por Resource, não só por Action. Permitir s3:GetObject em * ainda é privilégio excessivo se a aplicação só precisa de um bucket. Sempre que a API do serviço suportar, especifique o ARN exato.
  4. Adicione condições (Condition) quando possível. Restringir por IP de origem, exigir MFA, ou limitar por tag do recurso são camadas adicionais que reduzem o impacto de uma credencial vazada.

4. Exemplo prático: de permissivo a mínimo

Política de partida, comum em código copiado de um tutorial genérico:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": "*" } ] } 

Versão com least privilege, depois de identificar que a aplicação só lê objetos de um bucket específico, sob um prefixo específico:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::app-uploads-prod", "arn:aws:s3:::app-uploads-prod/incoming/*" ], "Condition": { "StringEquals": { "aws:PrincipalTag/environment": "production" } } } ] } 

Em Terraform, o mesmo desenho, evitando reescrever o JSON manualmente:

data "aws_iam_policy_document" "app_uploads_read" { statement { effect = "Allow" actions = [ "s3:GetObject", "s3:ListBucket", ] resources = [ aws_s3_bucket.app_uploads.arn, "${aws_s3_bucket.app_uploads.arn}/incoming/*", ] condition { test = "StringEquals" variable = "aws:PrincipalTag/environment" values = ["production"] } } } resource "aws_iam_policy" "app_uploads_read" { name = "app-uploads-read-only" policy = data.aws_iam_policy_document.app_uploads_read.json } 

5. Automatizando a auditoria de acessos

Desenhar a política mínima na criação não é suficiente — permissões tendem a acumular ao longo do tempo, e é preciso um processo contínuo para detectar isso:

  • IAM Access Analyzer identifica automaticamente recursos acessíveis por entidades externas à conta (buckets, roles, chaves KMS) e também gera relatórios de "permissões concedidas versus permissões usadas nos últimos 90 dias", apontando o que pode ser removido:
aws accessanalyzer list-findings \ --analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/default \ --filter '{"status": {"eq": ["ACTIVE"]}}' 
  • cloudsplaining, uma ferramenta open source, escaneia políticas IAM de uma conta AWS inteira e sinaliza padrões arriscados (wildcards, privilege escalation, ausência de resource constraints) em um relatório HTML:
pip install cloudsplaining cloudsplaining download --profile production cloudsplaining scan --input-file default.json --output results/ 
  • Integrando com o pipeline de IaC: o mesmo conftest/OPA visto no artigo anterior desta série pode avaliar documentos de política IAM gerados pelo Terraform antes do apply, bloqueando qualquer "Resource": "*" combinado com uma action de escrita, por exemplo:
package terraform.iam deny[msg] { resource := input.resource_changes[_] resource.type == "aws_iam_policy" statement := resource.change.after.policy.Statement[_] statement.Effect == "Allow" statement.Resource == "*" msg := sprintf( "política '%s' usa Resource=\"*\" — restrinja ao ARN específico", [resource.address], ) } 

6. Rotação e identidades temporárias

Além de restringir o que uma credencial pode fazer, reduzir por quanto tempo ela é válida limita ainda mais o impacto de um vazamento:

  • Prefira roles assumidas via sts:AssumeRole a usuários IAM com access key fixa sempre que possível.
  • Em pipelines de CI, use OIDC federation (GitHub Actions, GitLab CI já suportam nativamente) para que o pipeline assuma uma role temporária sem nenhuma credencial de longa duração armazenada como secret:
# GitHub Actions assumindo uma role via OIDC, sem access key fixa permissions: id-token: write contents: read steps: - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy aws-region: us-east-1 

7. Conclusão da série

Segredos guardados corretamente, recursos validados por política antes do apply e permissões desenhadas com o mínimo privilégio necessário são três frentes independentes, mas que se reforçam: um segredo vazado importa menos se a credencial associada tem alcance limitado; uma política de IAM bem desenhada é, ela mesma, um alvo natural de checagem via Policy as Code. Nenhuma automação de infraestrutura está completa sem tratar segurança e governança como parte do pipeline, não como uma revisão manual que acontece (ou não) antes de ir para produção.

Imagem de capa: Logo oficial da HashiCorp, mantenedora do Vault — Wikimedia Commons

Referências:

  1. AWS IAM — Grant Least Privilege
  2. AWS IAM Access Analyzer
  3. cloudsplaining — Documentation