"Effect": "Allow",
Em 22 de dezembro de 2021, a AWS lançou uma nova versão (número 20) da policy AWSSupportServiceRolePolicy, que é usada pela Role AWSServiceRoleForSupport.
Nunca ouviu falar delas? Elas estão na sua conta aqui:
- Policy AWSSupportServiceRolePolicy
- Role AWSServiceRoleForSupport
Se não quiser clicar, isso é o que você vai encontrar lá:
Ou seja, role e policy internas da AWS; você não pode usar, não pode excluir, não pode fazer nada além de simplesmente saber que elas existem e o que elas fazem.
Não sei se você sabe, mas é possível ver o histórico e as versões anteriores de uma policy no IAM:
A versão 19 é a atualmente em uso.
Vamos olhar a versão 20. Ela é bem comprida, vou suprimir algumas partes:
{
"Statement": [
...(bloco irrelevante)...
{
"Action": [
...(centenas de entradas antes)...
"s3:getObject",
...(centenas de entradas depois)...
],
"Effect": "Allow",
"Resource": [
"*"
]
}
],
"Version": "2012-10-17"
}
Caso não tenha conseguido perceber nade de errado, vou decompor o problema aqui em duas etapas:
- Action:
"Action": [
"s3:getObject",
],
"Effect": "Allow",
O que s3:getObject faz? Para isso, precisamos olhar a API:
Retrieves objects from Amazon S3.
Ou seja, Allow para s3:getObject faz com que quem quer que tenha esta policy possa acessar objetos em Buckets S3. Mas onde?
- Resource:
"Resource": [
"*"
]
A resposta para onde é: em todos os lugares.
Todos os buckets S3.
De todos os usuários.
Como se pode imaginar, nem todo muito ficou muito feliz ao ficar sabendo dessa mudança:
Se ficou interessado em saber como notar modificações desse tipo, outros profissionais de segurança criaram um bot que monitora modificações em policies desse tipo e tweetam sobre.
Como se pode imaginar, muitas pessoas não ficaram muito satisfeitas ao descobrir isso, então a AWS fez um "rollback" da atualização - por isso a versão default está marcada para 19 em vez da mais nova, 20.
Pena que isso demorou cerca de 10 horas para acontecer.
Eventualmente a AWS lançou um Security Bulletin a respeito, indicando que a Role é usada por um conjunto limitado de sistemas de suporte e que não ofereciam a possibilidade do uso desse recurso:
While these permissions were temporarily present, they were not and could not be used - only a tightly controlled set of AWS support systems may assume the AWSSupportService role, and these systems do not provide the capability to access S3 objects even if permission is granted to the role.
Esse evento em particular é um excelente motivador para explorar dois aspectos extremamente relevantes sobre segurança em provedores de Cloud:
Como detectar se meus objetos foram acessados por alguém fora da organização?
Como impedir acessos indevidos, ainda que dos próprios administradores do serviço?
Detecção de acessos
Se o conteúdo de um arquivo é considerado sensível, tão importante quanto a segurança do armazenamento é a auditoria dos acessos a tal arquivo. Como fazer para avaliar acessos a arquivos em Buckets S3?
CloudTrail
A maneira oficial de detectar ações usando a API da AWS (como, por exemplo, uma chamada a s3:getObject) é usando o CloudTrail.
Há um pequeno inconveniente aqui: a chamada de API que registra acesso a objetos no S3 é considerada um Data Event, e, como descrito na documentação:
By default, trails do not log data events. Additional charges apply for data events.
Logs dessa natureza não são habilitados por padrão, e geram custos adicionais - que podem atingir valores expressivos dependendo do número de objetos e frequência de acesso.
Eventos de dados entregues ao S3: 0,10 USD por 100.000 eventos de dados entregues
Como está aquele budget de segurança?
Server Access Logs
Um outro recurso de segurança que poderia ser usado é o Server Access Logging do S3. Logs deste tipo são entregues para um outro bucket S3 para ser analisado. Mas, novamente, estes logs não são ativados por padrão, você precisa fazê-lo em cada Bucket. Pelo menos não há nenhum custo extra - exceto o óbvio armazenamento dos arquivos de log gerados.
Um pequeno inconveniente aqui: este é um exemplo de log de acessos, extraído da própria documentação
79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be awsexamplebucket1 [06/Feb/2019:00:00:38 +0000] 192.0.2.3 79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be 3E57427F3EXAMPLE REST.GET.VERSIONING - "GET /awsexamplebucket1?versioning HTTP/1.1" 200 - 113 - 7 - "-" "S3Console/0.4" - s9lzHYrFp76ZVxRcpX9+5cjAnEH2ROuNkd2BHfIa6UkFVdtjf5mKR3/eTPFvsiP/XV/VLi31234= SigV2 ECDHE-RSA-AES128-GCM-SHA256 AuthHeader awsexamplebucket1.s3.us-west-1.amazonaws.com TLSV1.1
79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be awsexamplebucket1 [06/Feb/2019:00:00:38 +0000] 192.0.2.3 79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be 891CE47D2EXAMPLE REST.GET.LOGGING_STATUS - "GET /awsexamplebucket1?logging HTTP/1.1" 200 - 242 - 11 - "-" "S3Console/0.4" - 9vKBE6vMhrNiWHZmb2L0mXOcqPGzQOI5XLnCtZNPxev+Hf+7tpT6sxDwDty4LHBUOZJG96N1234= SigV2 ECDHE-RSA-AES128-GCM-SHA256 AuthHeader awsexamplebucket1.s3.us-west-1.amazonaws.com TLSV1.1
79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be awsexamplebucket1 [06/Feb/2019:00:00:38 +0000] 192.0.2.3 79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be A1206F460EXAMPLE REST.GET.BUCKETPOLICY - "GET /awsexamplebucket1?policy HTTP/1.1" 404 NoSuchBucketPolicy 297 - 38 - "-" "S3Console/0.4" - BNaBsXZQQDbssi6xMBdBU2sLt+Yf5kZDmeBUP35sFoKa3sLLeMC78iwEIWxs99CRUrbS4n11234= SigV2 ECDHE-RSA-AES128-GCM-SHA256 AuthHeader awsexamplebucket1.s3.us-west-1.amazonaws.com TLSV1.1
Os cinco primeiros campos do log são:
- Identidade do Bucket Owner (você)
- Bucket
- Timestamp
- Endereço IP do cliente
- Identidade do cliente que gerou a requisição (ou - para requisições não autenticadas).
Olhando de perto especificamente os campos de identidade da primeira linha, temos:
- ID do Bucket Owner:
79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be
- ID do Cliente:
79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be
Neste caso aqui está bem fácil de identificar quem realizou o acesso, já que os IDs são iguais. Mas... uma pergunta legítima: que ID é esse? Não parece com nada que a AWS apresenta em outros lugares, certo?
É porque realmente não é. O formato do ID registrado pelo S3 é chamado de Canonical user ID. Como a própria AWS descreve:
that is an obfuscated form of the AWS account ID.
Traduzindo: uma forma obfuscada do AWS Account ID.
É possível identificar qual o Canonical user ID da sua conta AWS seguindo esta documentação. Mas como identificar de que conta é um Canonical ID? Bem, isso não tem na documentação.
Concluindo, os Server Access Logs podem até ajudar a detectar acessos "estranhos" aos arquivos, mas não vão dizer o mais importante: de quem.
(Se você souber como fazer a engenharia reversa de um Canonical User ID me avisa nos comentários!)
Mitigação
Espere o melhor, mas assuma o pior: segurança em camadas é a prática na qual diversos controles de segurança são implementados.
No caso descrito no começo deste post, a Role é criada e mantida pela AWS. Mas uma organização certamente conta com dezenas (centenas?) de Roles. E se uma delas sofrer uma modificação que garanta permissões indevidas? Quais as principais opções para mitigar um possível acesso indevido via policies e roles que, a princípio, são "legítimas"?
Análise e remoção de roles
Toda Role na AWS conta com uma coluna de Last Activity. É uma boa prática reavaliar Roles criadas no seu ambiente e remover as que não são mais relevantes de tempos em tempos.
O IAM Access Analyzer ajuda a monitorar Buckets S3 e IAM roles que são compartilhadas com entidades externas.
De qualquer forma, este conselho não é muito útil para o caso em questão, já que não é possível excluir as roles e policies internas da AWS - as primeiras imagens deste post trazem essa informação.
Service Control Policies
O AWS Organizations conta com um recurso conhecido como SCP - Service Control Policies - no qual é possível indicar a "permissão máxima" passível de ser atribuída usando o IAM das contas-membro.
Este recurso poderia ser usado para limitar a criação de uma Role semelhante à mencionada neste post.
Entretanto, não serviria para mitigar os acessos da Role AWSServiceRoleForSupport.
Extraído da própria documentação:
SCPs do not affect any service-linked role.
A primeira imagem deste post descreve que a Role é uma Service Linked Role.
Bucket Policies
Alguns recursos da AWS contam com Resource-based policies, que são semelhantes às identity-based policies encontradas no IAM.
As Identity-based policies são associadas a usuários, grupos ou roles.
Resource-based policies estão diretamente ligadas a recursos na AWS, como chaves KMS, filas SQS e, felizmente, buckets S3.
(Ficou curioso para saber que outros recursos contam com Resource-based policies? Você pode conferir nesta tabela da documentação, adianto que são bem mais que você pensa!).
Para o problema apresentado - mitigar uma Role com privilégios "indevidos" -, bucket policies conseguiriam evitar acessos indevidos: ambos os conjuntos de permissões são avaliados em conjunto. Um explicit Deny sempre vence.
Criptografia de objetos com KMS
O S3 suporta a criptografia dos seus dados "at rest" por meio de várias estratégias diferentes.
Chaves de criptografia armazenadas no KMS também contam com Resource-based Policies, um usuário com permissão de s3:getObject também precisaria estar associado à ação kms:decrypt da key policy usada para os objetos, efetivamente mitigando acessos indevidos causados por uma Role mal configurada.
Mas atenção: como mencionei, existem várias estratégias diferentes! Dependendo da estratégia adotada, uma chave KMS sequer é usada (por exemplo, com SSE-S3, uma chave da AWS é usada, e a criptografia ocorre inteiramente em background, não mitigando o acesso).
TL;DR:
IAM é difícil - até a AWS se engana de vez em quando.
Mudanças devem sempre sofrer ampla revisão antes de serem aplicadas. Roles e policies devem ser constantemente revisitadas para verificar se ainda são relevantes.
Se você tem algo considerado sensível e usa armazenamento na nuvem, saiba exatamente quais as opções de segurança disponíveis ao seu acesso.
Top comments (0)