Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

Segurança e governança em infraestrutura - IAM com menor privilégio na prática

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 C

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
📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.