O problema das permissões excessivas no S3
Buckets do Amazon Simple Storage Service (Amazon S3) com configurações permissivas demais são um risco silencioso. Políticas de bucket ou Listas de Controle de Acesso (ACLs) muito abertas podem passar despercebidas por meses — até que um dado sensível seja exposto. A boa notícia é que a AWS documentou um fluxo de trabalho completo para identificar e remediar esse problema de forma estruturada.
O guia é voltado para engenheiros de segurança, arquitetos de nuvem e times de DevOps que gerenciam ambientes AWS com cargas de trabalho no S3 — tanto em conta única quanto em ambientes multi-account via AWS Organizations.
Visão geral da solução: cinco fases
A abordagem proposta pela AWS organiza o trabalho em cinco fases sequenciais:
- Fase 1 – Configuração e pré-requisitos: preparar o ambiente, designar uma conta central de segurança, implantar o AWS Config em todas as contas e habilitar o AWS Security Hub com administrador centralizado.
- Fase 2 – Detecção e identificação: implantar regras do AWS Config e uma função Lambda de auditoria que escaneia cada bucket verificando três pontos: configuração de bloqueio de acesso público, status da política do bucket e concessões via ACL.
- Fase 3 – Remediação: aplicar políticas restritivas de bucket, automatizar correções via AWS Lambda ou distribuir configurações padronizadas com AWS CloudFormation StackSets.
- Fase 4 – Monitoramento contínuo: agendar scans recorrentes via Amazon EventBridge, habilitar o IAM Access Analyzer para S3 e configurar alertas automáticos para novas violações.
- Fase 5 – Limpeza de recursos: revisar e remover os recursos criados durante a auditoria que não são mais necessários.
Pré-requisitos
Antes de começar, é necessário ter uma conta AWS com permissões para criar funções Lambda, papéis do AWS Identity and Access Management (IAM) e tópicos do Amazon Simple Notification Service (Amazon SNS). Também é preciso ter o AWS Command Line Interface (AWS CLI) ou o SDK instalado localmente. Em ambientes multi-account, o AWS Organizations já deve estar configurado. Familiaridade básica com políticas IAM e Python facilita a customização da solução.
Detecção: como o scanner funciona
O coração da solução é uma função Lambda escrita em Python usando a biblioteca Boto3. Ela percorre todos os buckets da conta e verifica três condições:
- Bloqueio de acesso público: todos os quatro parâmetros do Public Access Block devem estar habilitados (
BlockPublicAcls,BlockPublicPolicy,IgnorePublicAclseRestrictPublicBuckets). - Status da política do bucket: se a política estiver marcada como pública, o bucket é sinalizado.
- Concessões via ACL: se o ACL conceder acesso a
AllUsers(acesso público anônimo) ouAuthenticatedUsers(qualquer conta AWS), o bucket é considerado de risco.
A AWS disponibiliza dois scripts de exemplo. O primeiro (Script v1) é ideal para alertas imediatos via SNS quando problemas são detectados:
import boto3
import json
def lambda_handler(event, context):
s3 = boto3.client('s3')
sns = boto3.client('sns')
risky_buckets = []
errors = []
try:
buckets = s3.list_buckets()['Buckets']
except Exception as e:
return {'statusCode': 500, 'body': f'Failed to list buckets: {str(e)}'}
for bucket in buckets:
bucket_name = bucket['Name']
issues = []
try:
# Check Public Access Block — all four settings should be enabled
try:
pab = s3.get_public_access_block(Bucket=bucket_name)
config = pab['PublicAccessBlockConfiguration']
if not all([
config.get('BlockPublicAcls'), # Block new public ACLs
config.get('BlockPublicPolicy'), # Block new public bucket policies
config.get('IgnorePublicAcls'), # Ignore existing public ACLs
config.get('RestrictPublicBuckets') # Restrict access to public buckets
]):
issues.append('Public Access Block not fully enabled')
except s3.exceptions.NoSuchPublicAccessBlockConfiguration:
issues.append('No Public Access Block configured')
# Check bucket policy — flag if policy status is public
try:
policy_status = s3.get_bucket_policy_status(Bucket=bucket_name)
if policy_status['PolicyStatus']['IsPublic']:
issues.append('Bucket policy allows public access')
except s3.exceptions.NoSuchBucketPolicy:
pass # No bucket policy is acceptable
# Check bucket ACL
acl = s3.get_bucket_acl(Bucket=bucket_name)
for grant in acl.get('Grants', []):
grantee = grant.get('Grantee', {})
uri = grantee.get('URI', '')
# 'AllUsers' = anonymous public access
# 'AuthenticatedUsers' = any AWS account (still overly permissive)
if grantee.get('Type') == 'Group' and ('AllUsers' in uri or 'AuthenticatedUsers' in uri):
issues.append('Bucket ACL grants public access')
break
if issues:
risky_buckets.append({'bucket': bucket_name, 'issues': issues})
except Exception as e:
errors.append(f'{bucket_name}: {str(e)}')
# Send alert if risky buckets found
if risky_buckets:
message = f'Found {len(risky_buckets)} buckets with public access:\n\n'
for item in risky_buckets:
message += f" {item['bucket']}: {', '.join(item['issues'])}\n"
sns.publish(
TopicArn='arn:aws:sns:<REGION>:<ACCOUNT_ID>:<TOPIC_NAME>',
Subject='S3 Public Access Alert',
Message=message
)
return {
'statusCode': 200,
'body': json.dumps({
'risky_buckets': risky_buckets,
'errors': errors,
'total_checked': len(buckets)
})
}
O segundo (Script v2) gera relatórios em CSV e JSON para análise histórica e integração com ferramentas de BI:
import boto3
import csv
import json
import os
def lambda_handler(event, context):
s3 = boto3.client('s3')
buckets = s3.list_buckets()['Buckets']
full_access_buckets = []
for bucket in buckets:
bucket_name = bucket['Name']
try:
bucket_policy = s3.get_bucket_policy(Bucket=bucket_name)['Policy']
policy = json.loads(bucket_policy)
for statement in policy['Statement']:
if (statement['Effect'] == 'Allow' and
statement['Principal'] == '*' and
'Action' in statement and
's3:*' in statement['Action']):
full_access_buckets.append({'BucketName': bucket_name})
break
except s3.exceptions.ClientError as e:
if e.response['Error']['Code'] != 'NoSuchBucketPolicy':
print(f'Error checking bucket policy for {bucket_name}: {e}')
# Output CSV
csv_output = os.path.join('/tmp', 'full_access_buckets.csv')
with open(csv_output, 'w', newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=['BucketName'])
writer.writeheader()
writer.writerows(full_access_buckets)
# Output JSON
json_output = os.path.join('/tmp', 'full_access_buckets.json')
with open(json_output, 'w') as jsonfile:
json.dump(full_access_buckets, jsonfile, indent=2)
# Upload to Amazon S3
output_bucket = '<OUTPUT_BUCKET_NAME>'
s3.upload_file(csv_output, output_bucket, 'full_access_buckets.csv')
s3.upload_file(json_output, output_bucket, 'full_access_buckets.json')
return {
'statusCode': 200,
'body': json.dumps(f'CSV and JSON files uploaded to {output_bucket}')
}
Importante: esses scripts são exemplos de referência e não estão prontos para produção. Revise o tratamento de erros, logging e permissões antes de qualquer implantação.
Extensão para múltiplas contas
Os scripts acima escaneiam apenas a conta atual. Para cobrir contas-membro em uma organização, é necessário adicionar a lógica de AssumeRole. A função abaixo assume o papel IAM de auditoria em cada conta-membro e retorna um cliente S3 com credenciais temporárias:
import boto3
import os
def get_member_s3_clients():
"""
Assumes the cross-account audit role in each member account and returns
a list of (account_id, s3_client) tuples.
"""
sts = boto3.client('sts')
member_accounts = os.environ.get('<MEMBER_ACCOUNTS>', '').split(',')
cross_account_role_name = os.environ.get('<CROSS_ACCOUNT_ROLE_NAME>')
external_id = os.environ.get('<EXTERNAL_ID>')
clients = []
for account_id in member_accounts:
account_id = account_id.strip()
if not account_id:
continue
try:
assumed_role = sts.assume_role(
RoleArn=f'arn:aws:iam::{account_id}:role/{cross_account_role_name}',
RoleSessionName='S3AuditSession',
ExternalId=external_id
)
# Create S3 client with assumed credentials
s3_client = boto3.client(
's3',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
clients.append((account_id, s3_client))
except Exception as e:
print(f'Failed to assume role in account {account_id}: {e}')
return clients
Para iterar sobre as contas-membro no handler principal, substitua a chamada s3.list_buckets() por um loop sobre os clientes retornados por get_member_s3_clients(). O papel de execução da Lambda na conta central de segurança precisa ter permissão sts:AssumeRole para os ARNs dos papéis cross-account.
Remediação: corrigindo o problema
Bloqueio de acesso público no nível da conta
Antes de ajustar políticas individuais de bucket, a AWS recomenda habilitar o bloqueio de acesso público no nível da conta. Isso impede que qualquer bucket da conta se torne público, independentemente de políticas ou ACLs individuais. O comando AWS CLI abaixo aplica essa configuração — substitua <ACCOUNT_ID> pelo ID da sua conta:
aws s3control put-public-access-block \
--account-id <ACCOUNT_ID> \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
Em ambientes multi-account, essa configuração pode ser distribuída via CloudFormation StackSets ou por meio de Políticas de Controle de Serviço (SCPs) do AWS Organizations. Antes de habilitar, verifique se alguma carga de trabalho depende de acesso público ao bucket — como hospedagem de sites estáticos ou compartilhamento de datasets públicos.
Políticas de bucket restritivas
Para negar acesso público de leitura e escrita em um bucket específico, a AWS apresenta o seguinte exemplo de política — ajuste o ARN do recurso, as ações e as condições conforme sua necessidade:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::<BUCKET_NAME>/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": ["public-read", "public-read-write"]
}
}
}
]
}
Para restringir o acesso apenas a usuários e papéis IAM específicos, o exemplo a seguir demonstra como estruturar a política — substitua <ACCOUNT_ID>, <USERNAME> e <ROLE_NAME>:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowObjectAccess",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<ACCOUNT_ID>:user/<USERNAME>",
"arn:aws:iam::<ACCOUNT_ID>:role/<ROLE_NAME>"
]
},
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
},
{
"Sid": "AllowBucketAccess",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<ACCOUNT_ID>:user/<USERNAME>",
"arn:aws:iam::<ACCOUNT_ID>:role/<ROLE_NAME>"
]
},
"Action": ["s3:ListBucket", "s3:GetBucketLocation"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>"
}
]
}
Verificação e monitoramento contínuo
Após aplicar as correções, a AWS recomenda um processo de verificação antes de partir para o monitoramento contínuo:
- Re-executar a função Lambda de auditoria para confirmar que os buckets sinalizados não aparecem mais na lista de risco.
- Verificar no Security Hub se o status de conformidade mudou de
FAILEDparaPASSEDnos controles relacionados ao S3. - Validar com o IAM Access Analyzer se as descobertas de acesso externo foram resolvidas.
- Testar as aplicações para garantir que cargas de trabalho legítimas continuam funcionando corretamente.
Para monitoramento contínuo, a função Lambda de auditoria pode ser agendada via Amazon EventBridge com regras de agendamento — por exemplo, diariamente às 6h UTC ou semanalmente às segundas-feiras. Além disso, o IAM Access Analyzer pode ser habilitado para monitorar continuamente políticas de bucket, ACLs e pontos de acesso, identificando buckets acessíveis de fora da sua conta ou organização.
Considerações de custo
Os principais geradores de custo nessa solução são o AWS Config e o Security Hub, que escalam com o número de contas e recursos monitorados. Lambda, EventBridge, SNS e S3 tendem a adicionar custos mínimos para a maioria dos ambientes. A AWS recomenda começar com um piloto em uma ou duas contas para validar os custos antes de escalar. Consulte as páginas de preços dos serviços e use a Calculadora de Preços da AWS para estimar os valores no seu ambiente específico. Para o IAM Access Analyzer, verifique a página de preços do IAM Access Analyzer para entender quais funcionalidades têm custo associado.
Boas práticas para manter a postura de segurança
- Comece pelos controles no nível da conta: habilite o S3 Block Public Access na conta. Em ambientes multi-account, aplique via SCPs do AWS Organizations.
- Automatize a detecção: use o IAM Access Analyzer para detectar acesso externo e agende a Lambda de auditoria com EventBridge para capturar novos problemas regularmente. Compare os resultados com a linha de base anterior para identificar desvios.
- Padronize entre contas: use CloudFormation StackSets para distribuir a mesma configuração segura para todas as contas da organização — papéis IAM, regras do AWS Config e configurações de bloqueio de acesso público.
- Medidas adicionais: revise e rotacione periodicamente as credenciais de papéis IAM cross-account e os External IDs; implemente criptografia server-side (SSE-S3 ou SSE-KMS) para dados em repouso; habilite logs de acesso ao S3 e eventos de dados do AWS CloudTrail para trilhas de auditoria.
Limpeza de recursos
Ao concluir a auditoria, revise e remova os recursos criados que não são mais necessários: funções Lambda e papéis IAM, regras do EventBridge, tópicos e assinaturas do SNS, buckets S3 com arquivos de saída da auditoria, regras e gravadores do AWS Config (se não forem mais necessários para conformidade) e o Security Hub (se habilitado exclusivamente para essa auditoria). Antes de deletar, verifique se os recursos não são utilizados por outras cargas de trabalho e retenha os resultados necessários.
Para saber mais
A AWS disponibiliza documentação complementar para aprofundamento:
- Revisando o acesso a buckets com o IAM Access Analyzer para Amazon S3
- Boas práticas de segurança para o Amazon S3
- Regras customizadas do AWS Config
- Controles do AWS Security Hub para Amazon S3
- Verificações de segurança do AWS Trusted Advisor — Permissões de Buckets S3
- Bloqueando o acesso público ao seu armazenamento Amazon S3
- Boas práticas de segurança do IAM
Fonte
Securing your Amazon S3 buckets: Identifying and remediating over-permissioned access (https://aws.amazon.com/blogs/security/securing-your-amazon-s3-buckets-identifying-and-remediating-over-permissioned-access/)
Leave a Reply