IAM Avançado: Fundamentos e Estratégias de Controle de Acesso
O Identity and Access Management (IAM) é o alicerce de qualquer arquitetura de segurança na AWS. Diferentemente de um sistema simples de usuário e senha, o IAM oferece um modelo sofisticado de controle baseado em políticas, roles e permissões granulares. Nesta seção, vamos além dos conceitos básicos e explorar estratégias avançadas que grandes organizações utilizam para manter segurança e escalabilidade.
A estrutura do IAM repousa em três elementos principais: usuários, grupos e roles. Porém, o que torna o IAM verdadeiramente poderoso é a capacidade de criar políticas (policies) que definem exatamente quais ações um principal (user, group ou role) pode executar em quais recursos, sob quais condições. A grande maioria dos problemas de segurança em ambientes AWS não ocorre porque as pessoas não sabem criar políticas, mas porque elas criam políticas excessivamente permissivas. O princípio do menor privilégio é não apenas uma recomendação — é uma obrigação se você deseja construir um ambiente verdadeiramente seguro.
Estrutura de Políticas e Avaliação de Permissões
Políticas IAM são documentos JSON que definem permissões. Elas possuem uma estrutura clara: efeito (Allow ou Deny), ação (qual serviço e qual operação), recurso (sobre o quê) e condições (sob quais circunstâncias). Um ponto crítico que muitos iniciantes não entendem é que a AWS avalia permissões seguindo uma lógica de negação implícita: por padrão, tudo é negado. Se você não tem uma política que explicitamente permite uma ação, ela será bloqueada.
Vejamos um exemplo prático. Imagine um desenvolvedor que precisa gerenciar apenas buckets S3 de uma aplicação específica, mas não pode listar todos os buckets da conta:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListSpecificBuckets",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketVersioning",
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::app-production-*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "us-east-1"
}
}
},
{
"Sid": "ObjectOperations",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::app-production-*/*"
},
{
"Sid": "DenyUnencryptedTransport",
"Effect": "Deny",
"Action": "s3:*",
"Resource": "*",
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
Note três padrões importantes aqui: (1) uso de wildcards nos nomes de recursos para escalar sem criar centenas de políticas, (2) separação lógica de permissões em múltiplos statements com SIDs descritivos, e (3) uso de Deny explícito para forçar boas práticas, como obrigar HTTPS. A instrução Deny sempre vence qualquer Allow, então é uma ferramenta poderosa para garantir conformidade.
Assumindo Roles: Conceito de Trust Policy
Enquanto as políticas de permissão (permission policies) definem o que uma role pode fazer, a política de confiança (trust policy) define quem pode assumir essa role. Muitos engenheiros cometem o erro de ignorar trust policies ou as deixam desnecessariamente abertas. Uma trust policy mal configurada é como ter uma porta de segurança perfeita mas deixar a chave na fechadura.
Considere este cenário real: uma função Lambda precisa acessar dados em DynamoDB, mas apenas quando invocada por um API Gateway específico. A trust policy da role da Lambda garantiria que apenas o serviço Lambda pode assumi-la. Assim:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "123456789012"
}
}
}
]
}
Este simples documento garante que a role só pode ser assumida pelo serviço Lambda na sua conta específica, não por qualquer outro principal de qualquer outra conta.
Controle de Acesso Baseado em Atributos (ABAC)
Uma abordagem moderna e altamente escalável é o ABAC (Attribute-Based Access Control). Ao invés de criar roles diferentes para cada combinação de projeto, ambiente e função, você cria uma única role e usa tags para controlar o acesso. Isso reduz exponencialmente a complexidade operacional.
Imagine uma organização com 20 projetos, 3 ambientes (dev, staging, prod) e 4 funções (dev, qa, devops, admin). Com RBAC tradicional, você precisaria de potencialmente 240 roles diferentes. Com ABAC, você tem uma role por tipo de função, e as tags definem quem pode acessar o quê:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "arn:aws:ec2:*:123456789012:instance/*",
"Condition": {
"StringEquals": {
"aws:PrincipalTag/Department": "${aws:ResourceTag/Department}",
"aws:PrincipalTag/Environment": "${aws:ResourceTag/Environment}"
}
}
}
]
}
Este statement permite que um usuário inicie ou pare instâncias EC2 apenas se sua tag "Department" corresponder à tag "Department" da instância e sua tag "Environment" corresponder. Um desenvolvedor do projeto "payments" no ambiente "dev" pode manipular instâncias que também têm essas mesmas tags, mas não pode tocar em instâncias do "billing" ou ambiente "prod". Essa abordagem escala naturalmente conforme você adiciona novos projetos e ambientes.
Service Control Policies (SCPs): Governança em Escala
Service Control Policies são um mecanismo de governança que funciona em nível de organização AWS, não em nível de usuário. Enquanto as políticas IAM tradicionais definem o que um usuário pode fazer, SCPs definem o teto máximo de permissões para uma conta inteira ou unidade organizacional. Essa distinção é crucial: um SCP nunca concede permissão, apenas limita.
SCPs funcionam como um filtro: uma ação será permitida apenas se ela for permitida tanto pela política IAM do usuário quanto pelo SCP aplicado à conta ou OU. Se qualquer um dos dois a bloquear, a ação é negada. Isso é especialmente poderoso em ambientes com múltiplas contas, onde você precisa garantir que mesmo que um desenvolvedor tenha permissões amplas em sua conta, ele não possa fazer certas coisas globalmente perigosas, como desabilitar CloudTrail ou deletar backups críticos.
Estratégias Comuns de SCPs
Uma estratégia comum é criar um SCP que bloqueia regiões fora do escopo da sua organização. Se sua empresa opera apenas em us-east-1, eu-west-1 e ap-southeast-1, não há razão para permitir launches em outras regiões, nem que acidentalmente nem que maliciosamente:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAllOutsideAllowedRegions",
"Effect": "Deny",
"NotAction": [
"cloudfront:*",
"iam:*",
"organizations:*",
"support:*",
"budgets:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"us-east-1",
"eu-west-1",
"ap-southeast-1"
]
}
}
}
]
}
Os serviços listados em NotAction são globais e não estão vinculados a regiões, portanto são exceções. Qualquer tentativa de, digamos, criar uma instância EC2 em us-west-2 será bloqueada independentemente das permissões IAM do usuário.
Prevenção de Ações Críticas de Segurança
Outro padrão essencial é bloquear alterações em serviços críticos de conformidade e segurança. CloudTrail, CloudWatch Logs, AWS Config e GuardDuty nunca devem ser desabilitados ou deletados, nem mesmo por administradores. Um SCP pode garantir isso:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventCloudTrailModification",
"Effect": "Deny",
"Action": [
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging",
"cloudtrail:UpdateTrail"
],
"Resource": "*"
},
{
"Sid": "PreventConfigDisable",
"Effect": "Deny",
"Action": [
"config:DeleteConfigurationRecorder",
"config:StopConfigurationRecorder",
"config:DeleteDeliveryChannel"
],
"Resource": "*"
},
{
"Sid": "PreventGuardDutyDisable",
"Effect": "Deny",
"Action": [
"guardduty:DeleteDetector",
"guardduty:DisassociateFromMasterAccount",
"guardduty:UpdateDetector"
],
"Resource": "*"
},
{
"Sid": "DenyLogsModification",
"Effect": "Deny",
"Action": [
"logs:DeleteLogGroup",
"logs:DeleteLogStream",
"logs:DeleteRetentionPolicy"
],
"Resource": "arn:aws:logs:*:*:log-group:/aws/cloudtrail/*"
}
]
}
Este SCP protege os trilhos de auditoria da sua organização. Se alguém (mesmo um admin da conta) tentar deletar o trail do CloudTrail, a ação será bloqueada. Isso é especialmente importante em cenários de investigação forense ou conformidade regulatória, onde você precisa garantir que ninguém, nem mesmo pessoas bem-intencionadas, possa "limpar" os logs.
Implementação e Manutenção de SCPs
SCPs são aplicados através de AWS Organizations. Você cria políticas na raiz da organização, em unidades organizacionais (OUs) específicas, ou em contas individuais. Uma boa prática é estruturar suas contas em OUs semânticas: Development, Staging, Production, Finance, etc. Depois, você aplica SCPs diferentes a cada OU segundo suas necessidades de segurança.
Um exemplo de estrutura organizacional:
Root (Organization)
├── OU: Workloads
│ ├── Development (SCP: Mais permissivo, apenas bloqueia ações globalmente perigosas)
│ ├── Staging (SCP: Moderado)
│ └── Production (SCP: Altamente restritivo)
├── OU: Security
│ └── Security-Audit (SCP: Denies de modificação de recursos críticos)
└── OU: Finance
└── Finance-Prod (SCP: Apenas serviços de billing, nada de compute)
A implementação em código usando Terraform (ferramenta comum para IaC) seria:
resource "aws_organizations_policy" "prevent_dangerous_actions" {
name = "PreventDangerousActions"
description = "Blocks globally dangerous actions across all accounts"
type = "SERVICE_CONTROL_POLICY"
content = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "DenyRootAccountUsage"
Effect = "Deny"
Action = [
"iam:CreateAccessKey",
"iam:CreateLoginProfile",
"iam:CreateVirtualMFADevice"
]
Resource = "arn:aws:iam::*:root"
}
]
})
}
resource "aws_organizations_policy_attachment" "production_attachment" {
policy_id = aws_organizations_policy.prevent_dangerous_actions.id
target_id = aws_organizations_organizational_unit.production.id
}
GuardDuty: Detecção de Ameaças com Machine Learning
GuardDuty é um serviço gerenciado de detecção de ameaças que analisa continuamente atividades anômalas em sua conta AWS usando machine learning. Ele processa dados de múltiplas fontes (VPC Flow Logs, CloudTrail, DNS logs) para identificar padrões suspeitos que poderiam indicar comprometimento, atividade maliciosa ou abuso de credenciais. Diferentemente de um firewall que trabalha em nível de rede, GuardDuty trabalha em nível de comportamento.
O grande valor do GuardDuty é que você não precisa ser um especialista em segurança para aproveitá-lo. Os modelos de machine learning treinados pela AWS reconhecem padrões como: acesso SSH por força bruta, atividade de criptominação, comunicação com IPs de botnet conhecidos, exclusão em massa de backups (sinal clássico de ataque ransomware), mudanças suspeitas em credenciais, e muito mais. Tudo isso funciona 24/7 sem que você precise escrever regras customizadas ou alimentar o sistema com dados de treinamento.
Ativação e Configuração Inicial
GuardDuty é ativado por conta e por região. Uma prática recomendada é habilitar GuardDuty em todas as regiões onde você opera, ou pelo menos em todas as regiões que você planeja usar. A ativação pode ser feita via console, CLI ou Infrastructure as Code.
Aqui está como habilitar GuardDuty programaticamente usando AWS CLI:
# Habilitar GuardDuty na região us-east-1
aws guardduty create-detector \
--enable \
--finding-publishing-frequency FIFTEEN_MINUTES \
--region us-east-1
# Habilitar em múltiplas regiões
for region in us-east-1 eu-west-1 ap-southeast-1; do
aws guardduty create-detector \
--enable \
--finding-publishing-frequency FIFTEEN_MINUTES \
--region $region
done
O parâmetro finding-publishing-frequency controla com qual frequência o GuardDuty atualiza os findings. As opções são FIFTEEN_MINUTES, ONE_HOUR, ou SIX_HOURS. Para ambientes críticos, FIFTEEN_MINUTES é o padrão.
Tipos de Findings e Interpretação
GuardDuty classifica descobertas (findings) em categorias amplas baseadas em tipo de ameaça. Há três categorias principais: Reconnaissance (reconhecimento), Instance Compromise (comprometimento de instância), e IAM Compromise (comprometimento de IAM). Cada uma tem subtipos que indicam a natureza específica da ameaça.
Um exemplo de finding real pode parecer assim (em JSON):
{
"FindingId": "1a234567890abcdef123456789012abc",
"Type": "Trojan.EC2/BlackholeTraffic.EC2",
"Severity": 8.3,
"CreatedAt": "2024-01-15T10:30:45.000Z",
"UpdatedAt": "2024-01-15T10:30:45.000Z",
"Resource": {
"ResourceType": "Instance",
"InstanceDetails": {
"InstanceId": "i-0123456789abcdef0",
"NetworkInterfaces": [
{
"PrivateIpAddresses": ["10.0.0.50"],
"PublicIp": "203.0.113.42"
}
]
}
},
"ServiceDetails": {
"ServiceName": "guardduty",
"DetectionDetails": {
"Threat": [
{
"Name": "Blackhole",
"ItemCount": 2847,
"FilePaths": []
}
]
}
},
"Description": "EC2 instance 'i-0123456789abcdef0' is communicating with 2847 different IP addresses that are known to be part of a botnet. Your instance might be compromised."
}
Este finding indica que uma instância EC2 está enviando tráfego para endereços conhecidos por fazerem parte de uma botnet. A severidade é 8.3 em 10, indicando uma ameaça séria. O tipo "Trojan.EC2/BlackholeTraffic.EC2" especifica que é uma instância EC2 possivelmente infectada com malware que contacta servidores de comando e controle. Ações imediatas seriam necessárias: isolar a instância, revisar logs, investigar o que foi instalado e quando.
Integração com Outras Ferramentas de Resposta
GuardDuty não é um silo isolado. Seus findings devem fluir para ferramentas de orquestração e resposta. A integração mais comum é com Amazon EventBridge, que pode disparar ações automáticas quando findings são gerados.
Aqui está um exemplo usando EventBridge para enviar findings críticos do GuardDuty para um tópico SNS e para um bucket S3 para auditoria:
import json
import boto3
from datetime import datetime
events_client = boto3.client('events')
sns_client = boto3.client('sns')
s3_client = boto3.client('s3')
def create_guardduty_event_rule():
"""
Cria uma regra EventBridge que captura findings críticos do GuardDuty
"""
rule_name = 'guardduty-critical-findings'
# Define o padrão de eventos: apenas findings com severidade >= 7
event_pattern = {
"source": ["aws.guardduty"],
"detail-type": ["GuardDuty Finding"],
"detail": {
"severity": [{"numeric": [">=", 7]}],
"type": [
"IAMUser/UnusualBehavior",
"Trojan.EC2/BlackholeTraffic.EC2",
"Backdoor.EC2/C&CActivity.B",
"CryptoCurrency.EC2/BitcoinTool.B"
]
}
}
# Cria a regra
response = events_client.put_rule(
Name=rule_name,
EventPattern=json.dumps(event_pattern),
State='ENABLED',
Description='Capture critical GuardDuty findings and trigger remediation'
)
return response['RuleArn']
def add_sns_target_to_rule(rule_name, sns_topic_arn):
"""
Adiciona SNS como target da regra para alertar o time de segurança
"""
events_client.put_targets(
Rule=rule_name,
Targets=[
{
'Id': '1',
'Arn': sns_topic_arn,
'InputTransformer': {
'InputPathsMap': {
'finding_id': '$.detail.id',
'finding_type': '$.detail.type',
'severity': '$.detail.severity',
'instance_id': '$.detail.resource.instanceDetails.instanceId',
'account_id': '$.detail.accountId'
},
'InputTemplate': (
'"CRITICAL GuardDuty Finding: <finding_type> '
'(Severity: <severity>) on Instance <instance_id> '
'in Account <account_id>. Finding ID: <finding_id>"'
)
}
}
]
)
def lambda_handler(event, context):
"""
Handler Lambda que processa eventos do GuardDuty via EventBridge
"""
finding = event['detail']
# Extrai informações relevantes
finding_id = finding['id']
finding_type = finding['type']
severity = finding['severity']
account_id = finding['accountId']
# Log estruturado para auditoria
audit_log = {
'timestamp': datetime.utcnow().isoformat(),
'finding_id': finding_id,
'finding_type': finding_type,
'severity': severity,
'account_id': account_id,
'full_finding': finding
}
# Salva no S3 para análise forense posterior
s3_client.put_object(
Bucket='security-findings-archive',
Key=f'guardduty/{account_id}/{finding_id}.json',
Body=json.dumps(audit_log, indent=2),
ContentType='application/json'
)
# Responde com ações automáticas conforme a severidade
if severity >= 8.5:
# Para ameaças críticas, isola a instância imediatamente
if 'instanceDetails' in finding.get('resource', {}):
instance_id = finding['resource']['instanceDetails']['instanceId']
ec2_client = boto3.client('ec2')
try:
# Cria um security group vazio e isola a instância
sg_response = ec2_client.create_security_group(
GroupName=f'isolation-{finding_id}',
Description=f'Isolation SG for compromised instance {instance_id}',
VpcId=get_instance_vpc(instance_id)
)
sg_id = sg_response['GroupId']
ec2_client.modify_instance_attribute(
InstanceId=instance_id,
Groups=[sg_id]
)
print(f"Instance {instance_id} isolated with SG {sg_id}")
except Exception as e:
print(f"Error isolating instance: {str(e)}")
return {
'statusCode': 200,
'body': json.dumps('Finding processed and logged')
}
def get_instance_vpc(instance_id):
"""Helper para obter a VPC de uma instância"""
ec2_client = boto3.client('ec2')
response = ec2_client.describe_instances(InstanceIds=[instance_id])
return response['Reservations'][0]['Instances'][0]['VpcId']
Este código cria uma pipeline completa: (1) EventBridge captura findings críticos do GuardDuty, (2) SNS notifica o time de segurança imediatamente, (3) uma função Lambda processa o evento, loga para auditoria, e para ameaças críticas, isola automaticamente a instância modificando seu security group.
Correlação e Análise de Padrões
O verdadeiro poder do GuardDuty emerge quando você começa a correlacionar findings ao longo do tempo. Um único finding de "acesso SSH incomum" pode ser um falso positivo. Mas se você vê este finding seguido de "download de arquivo grande" e depois "comunicação com IP suspeito", você tem um padrão que indica comprometimento real.
Ferramentas como Amazon Security Hub agregam findings de múltiplos serviços (GuardDuty, Config, IAM Access Analyzer) em um dashboard centralizado e podem aplicar correlações automáticas. Se você estiver montando sua própria solução de correlação, um exemplo básico em Python seria:
import boto3
from collections import defaultdict
from datetime import datetime, timedelta
guardduty_client = boto3.client('guardduty')
def correlate_findings(detector_id, hours=24):
"""
Correlaciona findings do GuardDuty nos últimas 24 horas
para identificar padrões de ataque
"""
# Busca todos os findings ativos dos últimas 24 horas
cutoff_time = datetime.utcnow() - timedelta(hours=hours)
findings = guardduty_client.list_findings(
DetectorId=detector_id,
FindingCriteria={
'Criterion': {
'updatedAt': {
'Gte': int(cutoff_time.timestamp()) * 1000
},
'severity': {
'Gte': 4
}
}
}
)
finding_ids = findings['FindingIds']
if not finding_ids:
return None
# Obtém detalhes dos findings
finding_details = guardduty_client.get_findings(
DetectorId=detector_id,
FindingIds=finding_ids
)
# Agrupa por recurso para encontrar padrões
resource_patterns = defaultdict(list)
for finding in finding_details['Findings']:
resource_type = finding['Resource'].get('ResourceType', 'Unknown')
finding_type = finding['Type']
severity = finding['Severity']
timestamp = finding['CreatedAt']
if resource_type == 'Instance':
instance_id = finding['Resource']['InstanceDetails']['InstanceId']
resource_patterns[instance_id].append({
'type': finding_type,
'severity': severity,
'timestamp': timestamp
})
# Analisa padrões: uma instância com múltiplos findings críticos é suspeita
suspicious_patterns = {}
for instance_id, findings_list in resource_patterns.items():
if len(findings_list) >= 3: # 3 ou mais findings na mesma instância
avg_severity = sum(f['severity'] for f in findings_list) / len(findings_list)
if avg_severity >= 6: # Severidade média alta
suspicious_patterns[instance_id] = {
'count': len(findings_list),
'avg_severity': avg_severity,
'findings': findings_list
}
return suspicious_patterns
Este script identifica instâncias que tiveram múltiplos findings em um período curto de tempo com severidade elevada, aumentando a confiança de que algo está realmente errado.
Conclusão
Segurança em AWS não é um destino, é um processo contínuo de múltiplas camadas. Os três tópicos que cobrimos trabalham em sincronia: IAM fornece o mecanismo de controle de acesso granular, garantindo que usuários tenham apenas as permissões que precisam; SCPs garantem que mesmo com permissões amplas em uma conta, ações globalmente perigosas sejam bloqueadas, criando um limite superior de segurança; e GuardDuty detecta quando algo deu errado, notificando você de comportamentos anômalos que indicam compromisso ou abuso.
A lição fundamental é que segurança não vem de uma única ferramenta ou política, mas da combinação de prevenção (IAM + SCPs), detecção (GuardDuty) e resposta (automação com Lambda e EventBridge). Além disso, não existe política perfeita na primeira tentativa — você precisa revisar regularmente suas políticas IAM, ajustar SCPs conforme suas organizações evoluem, e iterativamente refinar suas regras de resposta automática do GuardDuty com base em findings que recebe. A segurança escala quando você para de pensar em "acesso de emergência" e começa a pensar em "acesso declarativo e verificável".