Criando uma Presigned URL no S3: Acesso Temporário a Arquivos Privados
Você tem um arquivo privado no S3 e precisa dar acesso temporário a um usuário sem expor suas credenciais AWS nem tornar o bucket público — esse é exatamente o cenário para o qual as Presigned URLs foram criadas. Com uma URL assinada, você delega acesso a um objeto específico por um período determinado, sem alterar as permissões do bucket.
TL;DR — Resumo Rápido
| Ponto | Detalhe |
|---|---|
| O que é | URL temporária que carrega credenciais assinadas na própria query string |
| Expiração padrão usada aqui | 3600 segundos (1 hora) |
| Quem assina | A identidade IAM que gera a URL (usuário, role ou instância) |
| Bucket precisa ser público? | Não — o objeto pode permanecer completamente privado |
| SDK usado | AWS SDK for Python (Boto3) e AWS SDK for JavaScript v3 |
| Alternativa via CLI | aws s3 presign |
Como a Presigned URL do S3 Funciona
Uma Presigned URL não é um token separado — ela é a própria requisição HTTP pré-assinada. O SDK pega as credenciais da identidade atual, calcula a assinatura SigV4 para a operação GetObject, e embute tudo isso nos parâmetros da URL. Quando o usuário acessa a URL, o S3 verifica a assinatura e o tempo de expiração antes de servir o objeto.
O ponto crítico: a verificação acontece no momento do acesso, não no momento da geração. Se a identidade que gerou a URL perder a permissão s3:GetObject antes da expiração, a URL para de funcionar imediatamente. O mesmo vale para políticas de bucket que bloqueiem o acesso.
sem chamada de rede SDK-->>App: Presigned URL (string) App-->>User: Envia a URL (e-mail, API, etc.) User->>S3: HTTP GET com assinatura na query string S3->>S3: Valida assinatura e expiração S3-->>User: 200 OK + conteúdo do objeto
- Aplicação chama o SDK com bucket, chave e expiração — o SDK assina localmente usando SigV4.
- Presigned URL é retornada para a aplicação e repassada ao usuário (e-mail, API response, etc.).
- Usuário faz um HTTP GET direto ao S3 — sem passar pela sua aplicação.
- S3 valida a assinatura e o tempo de expiração; se válidos, retorna o objeto.
Pense na Presigned URL como um voucher de entrada numerado: quem o possui entra, mas ele expira no horário impresso — independente de quem o emitiu ainda estar na empresa.
Pré-requisitos: Permissões IAM Necessárias
A identidade que gera a URL precisa ter permissão s3:GetObject no objeto alvo. Sem isso, a URL é gerada localmente pelo SDK (a assinatura é calculada offline), mas falha com 403 Forbidden quando o usuário tenta acessá-la.
Política mínima para geração e uso da Presigned URL:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::nome-do-seu-bucket/*"
}
]
}
Se o bucket tiver uma política que inclua um Deny explícito ou se o Block Public Access estiver configurado de forma restritiva, isso não afeta Presigned URLs — elas operam via autenticação SigV4, não via acesso público. Porém, um Deny explícito na bucket policy sobrescreve qualquer Allow na política IAM.
Gerando a Presigned URL para Download com Expiração de 1 Hora
Opção 1: Python com Boto3 (Recomendado)
O método generate_presigned_url do cliente S3 assina a operação get_object localmente. Nenhuma chamada de rede é feita durante a geração — a URL é calculada e retornada imediatamente.
import boto3
from botocore.exceptions import ClientError
def gerar_presigned_url(bucket_name: str, object_key: str, expiration: int = 3600) -> str:
"""
Gera uma Presigned URL para download de um objeto S3 privado.
:param bucket_name: Nome do bucket S3
:param object_key: Chave (caminho) do objeto no bucket
:param expiration: Tempo de expiração em segundos (padrão: 3600 = 1 hora)
:return: Presigned URL como string
"""
s3_client = boto3.client('s3', region_name='us-east-1')
try:
url = s3_client.generate_presigned_url(
ClientMethod='get_object',
Params={
'Bucket': bucket_name,
'Key': object_key
},
ExpiresIn=expiration
)
except ClientError as e:
raise RuntimeError(f'Erro ao gerar Presigned URL: {e}') from e
return url
# Exemplo de uso
if __name__ == '__main__':
url = gerar_presigned_url(
bucket_name='meu-bucket-privado',
object_key='relatorios/janeiro-2025.pdf'
)
print(f'URL válida por 1 hora:\n{url}')
Opção 2: JavaScript/TypeScript com AWS SDK v3
No SDK v3, a geração de Presigned URLs usa o pacote separado @aws-sdk/s3-request-presigner em conjunto com o comando GetObjectCommand.
🔽 Clique para expandir — Código JavaScript (AWS SDK v3)
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3Client = new S3Client({ region: 'us-east-1' });
async function gerarPresignedUrl(
bucketName: string,
objectKey: string,
expirationSeconds: number = 3600
): Promise<string> {
const command = new GetObjectCommand({
Bucket: bucketName,
Key: objectKey,
});
const url = await getSignedUrl(s3Client, command, {
expiresIn: expirationSeconds,
});
return url;
}
// Exemplo de uso
gerarPresignedUrl('meu-bucket-privado', 'relatorios/janeiro-2025.pdf')
.then((url) => console.log('Presigned URL:', url))
.catch((err) => console.error('Erro:', err));
Opção 3: AWS CLI
Para testes rápidos ou scripts de operação, a CLI resolve sem escrever código. O parâmetro --expires-in aceita segundos.
aws s3 presign s3://meu-bucket-privado/relatorios/janeiro-2025.pdf \
--expires-in 3600 \
--region us-east-1
A saída é a URL completa pronta para uso. Útil para validar se as permissões estão corretas antes de implementar no código.
Diagnóstico: Erros Comuns com Presigned URL no S3
O erro mais frequente em produção não é na geração — é um 403 silencioso quando o usuário tenta usar a URL. A armadilha clássica: o SDK gera a URL sem reclamar (a assinatura é local), mas a identidade não tem s3:GetObject no objeto. Você só descobre quando o usuário reporta que o download falhou.
s3:GetObject?"}; B -- Não --> C["Adicionar permissão
s3:GetObject na política IAM"]; B -- Sim --> D{"Bucket policy tem
Deny explícito?"}; D -- Sim --> E["Remover ou ajustar
o Deny na bucket policy"]; D -- Não --> F{"Credenciais temporárias
expiraram?"}; F -- Sim --> G["Reduzir ExpiresIn ou
renovar credenciais da role"]; F -- Não --> H{"Região do cliente
está correta?"}; H -- Não --> I["Instanciar cliente S3
com region_name correto"]; H -- Sim --> J["Verificar logs do S3
com CloudTrail"];
Verifique cada camada na ordem do diagrama. Um Deny explícito na bucket policy bloqueia mesmo com a política IAM correta — esse é o ponto que mais gera confusão.
Verificando permissões da identidade que gera a URL
# Identifica qual identidade está sendo usada pelo SDK/CLI
aws sts get-caller-identity
# Simula se a identidade tem s3:GetObject no objeto alvo
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/MinhaRole \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::meu-bucket-privado/relatorios/janeiro-2025.pdf \
--region us-east-1
Verificando a bucket policy
aws s3api get-bucket-policy \
--bucket meu-bucket-privado \
--region us-east-1
Problema de região: URL gerada na região errada
Se o cliente S3 for instanciado sem especificar a região, o SDK pode usar us-east-1 como padrão mesmo que o bucket esteja em outra região. O resultado é um erro de redirecionamento ou AuthorizationHeaderMalformed. Sempre instancie o cliente com a região correta do bucket.
Credenciais temporárias e expiração da role
Quando a URL é gerada por uma role assumida (ex: Lambda, EC2 com instance profile), as credenciais temporárias têm sua própria expiração. Se as credenciais da role expirarem antes da Presigned URL, o S3 retorna 403 mesmo que a URL ainda esteja dentro do prazo configurado. Defina ExpiresIn sempre menor ou igual ao tempo restante das credenciais temporárias.
Considerações de Segurança para Presigned URLs em Produção
- Não logue a URL completa — ela carrega a assinatura e funciona para qualquer pessoa que a possua durante o período de validade.
- Use o menor tempo de expiração possível para o seu caso de uso. Uma hora é razoável para downloads manuais; para fluxos automatizados, 5-15 minutos costuma ser suficiente.
- Não é possível revogar uma Presigned URL individualmente — para invalidação imediata, a única opção é remover a permissão
s3:GetObjectda identidade que a gerou, o que afeta todas as URLs geradas por ela. - HTTPS é obrigatório — o SDK gera URLs com
https://por padrão. Não force downgrade para HTTP.
Criando Presigned URL para Upload (PUT)
O mesmo mecanismo funciona para uploads. Troque get_object por put_object no Boto3, ou use PutObjectCommand no SDK v3. O usuário pode fazer um HTTP PUT diretamente para a URL sem passar pela sua aplicação.
import boto3
s3_client = boto3.client('s3', region_name='us-east-1')
url_upload = s3_client.generate_presigned_url(
ClientMethod='put_object',
Params={
'Bucket': 'meu-bucket-privado',
'Key': 'uploads/arquivo-do-usuario.pdf',
'ContentType': 'application/pdf'
},
ExpiresIn=3600
)
print(f'URL para upload direto ao S3:\n{url_upload}')
A identidade que gera a URL de upload precisa ter s3:PutObject no recurso alvo. O usuário que faz o upload não precisa de nenhuma credencial AWS.
Próximos Passos e Documentação
Presigned URLs cobrem bem o caso de acesso temporário a objetos individuais. Para cenários mais complexos — como acesso a múltiplos objetos ou distribuição com cache global — considere combinar com o CloudFront usando CloudFront Signed URLs, que operam em uma camada diferente e têm semântica de revogação distinta.
- Documentação oficial: Using presigned URLs — Amazon S3
- Boto3 Reference: generate_presigned_url
- AWS SDK v3: @aws-sdk/s3-request-presigner
Glossário
| Termo | Definição |
|---|---|
| Presigned URL | URL com assinatura SigV4 embutida que autoriza uma operação específica no S3 por tempo limitado |
| SigV4 | Algoritmo de assinatura criptográfica usado pela AWS para autenticar requisições HTTP |
| GetObject | Operação S3 que retorna o conteúdo de um objeto; é a operação assinada para URLs de download |
| ExpiresIn | Parâmetro que define o tempo de validade da URL em segundos a partir do momento da geração |
| Block Public Access | Configuração de bucket/conta que bloqueia acesso público anônimo; não afeta Presigned URLs autenticadas |
Comentários
Postar um comentário