Artigo

URL presinadas de S3: que son, como funcionan, límites de caducidade e exemplos de código

Que é unha URL presinada de S3, que significa cada parámetro X-Amz, o límite de 7 días, código de subida e descarga (CLI, boto3, JS) e erros comúns.

Unha URL presinada de S3 é unha ligazón temporal a un obxecto dun bucket privado. Leva unha sinatura na súa query string, así que quen a teña pode descargar (ou subir) ese obxecto sen credenciais de AWS ata que a ligazón caduque, e o bucket nunca se fai público. Créala coas túas propias chaves, no teu código ou desde a AWS CLI, e pode durar desde 1 segundo ata 7 días. Esta guía explica como funciona, que significa cada parámetro X-Amz-*, canto pode durar, código para descargas e subidas e os erros cos que máis probablemente te atoparás. Os límites están tomados da documentación de AWS e de Cloudflare en outubro de 2026.

URL presinadas de S3 dunha ollada

PreguntaResposta
Que permite?Unha operación (GET, PUT, HEAD, DELETE…) sobre un obxecto
Caducidade máxima7 días (604.800 segundos) con SigV4 e chaves dun usuario de IAM
Caducidade con chaves temporaisRemata cando remata a sesión do rol ou as credenciais STS, o que suceda antes
Rango na consola de S3De 1 minuto a 12 horas
Caducidade por defecto3.600 s na AWS CLI e boto3, 900 s no presigner de JavaScript v3
Pódese usar varias veces?Si, tantas veces como se queira ata que caduque
Pódese revogar unha URL?Non, só revogando as credenciais que a asinan ou denegándoa nunha política
Crear unha chama a S3?Non, a sinatura calcúlase localmente

Como funciona unha URL presinada

  1. O teu código (ou a CLI) toma o bucket, a chave do obxecto, o método HTTP e un tempo de caducidade.
  2. O SDK asina eses valores coa túa secret access key usando AWS Signature Version 4. Neste momento non se envía nada a S3.
  3. Dás a URL resultante a alguén: un navegador, unha compañeira, unha app móbil.
  4. Cando a usan, S3 volve calcular a sinatura coa secret que corresponde á access key da URL. Se coincide, a URL non caducou e quen a asinou pode facer esa operación, a petición pasa.

A URL leva os permisos de quen a asinou. AWS é explícito niso: se o usuario ou o rol de IAM que xerou a URL non pode ler o obxecto, a URL devolve 403 por moi válida que sexa a sinatura.

Que significan os parámetros X-Amz

Unha URL presinada con SigV4 ten este aspecto (con saltos de liña engadidos):

https://my-bucket.s3.eu-west-1.amazonaws.com/reports/q3.pdf
  ?X-Amz-Algorithm=AWS4-HMAC-SHA256
  &X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20261017%2Feu-west-1%2Fs3%2Faws4_request
  &X-Amz-Date=20261017T093000Z
  &X-Amz-Expires=3600
  &X-Amz-SignedHeaders=host
  &X-Amz-Signature=5f2c...e91a
ParámetroSignificado
X-Amz-AlgorithmO algoritmo de sinatura: AWS4-HMAC-SHA256 para SigV4
X-Amz-CredentialID da access key máis o ámbito: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request
X-Amz-DateCando se asinou a URL, en UTC, yyyyMMddTHHmmssZ
X-Amz-ExpiresCantos segundos é válida a URL despois de X-Amz-Date, de 1 a 604800
X-Amz-SignedHeadersCabeceiras cubertas pola sinatura, sempre polo menos host
X-Amz-SignatureA sinatura, 64 caracteres hexadecimais
X-Amz-Security-TokenSó aparece cando a URL se asinou con credenciais temporais (STS, roles, EC2, ECS, SSO)

Cambia calquera parte asinada (a chave, o método, a caducidade ou unha cabeceira asinada) e a sinatura deixa de coincidir. Fíxate tamén no que a URL revela en texto claro: o nome do bucket, a chave completa do obxecto e o ID da túa access key. A secret key nunca está na URL.

Canto dura unha URL presinada

O límite depende das credenciais que a asinan:

Asinada conValidez máxima
Access keys dun usuario de IAM (SigV4)7 días, porque a propia chave de sinatura de SigV4 vale 7 días
Sesión dun rol de IAM (STS AssumeRole)Ata que remata a sesión, 1 hora por defecto
Perfil de instancia EC2Ata que rotan as credenciais do rol, como moito unhas 6 horas
Rol de tarefa de ECSAta que rotan as credenciais da tarefa, normalmente cada 1 a 6 horas
Calquera credencial revogada ou borradaDeixa de funcionar ao momento, diga o que diga X-Amz-Expires
Consola de S3Escolles entre 1 minuto e 12 horas

Isto explica a sorpresa máis habitual: pides 7 días desde unha función Lambda ou unha instancia EC2 e a ligazón morre despois dunha hora ou de poucas horas. A URL só dura o que duran as credenciais que ten detrás. Se necesitas ligazóns longas, asina cun usuario de IAM dedicado que só teña acceso de lectura a ese bucket.

S3 comproba a caducidade cando comeza a petición. Unha descarga grande que empeza un segundo antes do prazo remata con normalidade, pero un reintento despois do prazo falla con AccessDenied e a mensaxe “Request has expired”. Pedir máis de 604.800 segundos rexéitase directamente.

Como xerar unha URL presinada

AWS CLI

aws s3 presign s3://my-bucket/reports/q3.pdf --expires-in 3600

O comando asina unha URL de descarga (GET). O valor por defecto son 3.600 segundos e o máximo 604.800. As versións recentes da CLI usan sempre SigV4, así que asegúrate de ter unha rexión configurada. Para URL de subida necesitas un SDK.

Python (boto3)

import boto3
from botocore.config import Config

s3 = boto3.client('s3', region_name='eu-west-1', config=Config(signature_version='s3v4'))

url = s3.generate_presigned_url(
    ClientMethod='get_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'reports/q3.pdf',
        'ResponseContentDisposition': 'attachment; filename="q3.pdf"',
    },
    ExpiresIn=3600,
)

Indica signature_version='s3v4' de forma explícita. A referencia de boto3 avisa de que, por compatibilidade, se non pode asinar coa antiga Signature Version 2 nas rexións que aínda a aceptan, o que descarta sen avisar algúns parámetros e cabeceiras como Range. ResponseContentDisposition é opcional: fai que o navegador garde o ficheiro cun nome limpo en vez de abrilo directamente.

JavaScript (AWS SDK v3)

import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const client = new S3Client({ region: 'eu-west-1' })

const url = await getSignedUrl(
  client,
  new GetObjectCommand({ Bucket: 'my-bucket', Key: 'reports/q3.pdf' }),
  { expiresIn: 3600 }
)

Se omites expiresIn, o presigner de JavaScript usa 900 segundos (15 minutos).

URL presinadas de subida (PUT)

A mesma idea funciona na outra dirección. Asinas un PutObject para unha chave e quen teña a URL pode escribir ese obxecto. O ficheiro vai directo desde o seu dispositivo ao bucket e o teu servidor nunca toca os bytes. Se xa existe un obxecto con esa chave, substitúese.

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const client = new S3Client({ region: 'eu-west-1' })

const url = await getSignedUrl(
  client,
  new PutObjectCommand({
    Bucket: 'my-bucket',
    Key: 'incoming/brief.pdf',
    ContentType: 'application/pdf',
  }),
  { expiresIn: 900, signableHeaders: new Set(['content-type']) }
)

await fetch(url, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/pdf' },
  body: file,
})

Engadir content-type a signableHeaders liga a URL a ese tipo, así que unha subida cun Content-Type distinto rexéitase. En boto3 o equivalente é generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), e o cliente ten que enviar a mesma cabeceira.

Tres cousas que revisar antes de subir desde un navegador:

  • CORS. O bucket necesita unha regra de CORS que permita a túa orixe e o método PUT. Para subidas multipart tamén ten que expoñer a cabeceira ETag, porque o navegador ten que lela de cada parte.
  • Chave fixa. Xera no servidor unha chave única (por exemplo cun prefixo UUID) para que un visitante non poida sobrescribir o ficheiro doutra persoa.
  • Sumas de comprobación. Os SDK actuais de AWS engaden por defecto unha suma CRC ás subidas. Se as subidas presinadas fallan contra un provedor compatible con S3 con erros de sinatura ou de suma de comprobación, configura requestChecksumCalculation: 'WHEN_REQUIRED' no cliente.

O POST presinado é un mecanismo distinto para formularios HTML simples, cunha política que pode limitar o tamaño e o tipo de contido. Non todos os provedores compatibles con S3 o admiten (R2 non), así que o PUT presinado é a opción máis portable.

Ficheiros grandes: subidas multipart presinadas

Un único PUT a S3 ten un tope de 5 GB, e AWS suxire multipart a partir duns 100 MB. Multipart divide o ficheiro en ata 10.000 partes, cada unha de 5 MiB a 5 GiB agás a última, que se suben de forma independente, en paralelo e con reintentos por parte. A presinatura funciona por parte: o servidor inicia a subida, asina unha URL UploadPart por cada parte, o navegador envía cada anaco e devolve o seu ETag, e o servidor completa a subida.

import {
  CreateMultipartUploadCommand,
  UploadPartCommand,
  CompleteMultipartUploadCommand,
} from '@aws-sdk/client-s3'

const { UploadId } = await client.send(
  new CreateMultipartUploadCommand({ Bucket: 'my-bucket', Key: 'video/raw.mov' })
)

const partUrl = await getSignedUrl(
  client,
  new UploadPartCommand({ Bucket: 'my-bucket', Key: 'video/raw.mov', UploadId, PartNumber: 1 }),
  { expiresIn: 3600 }
)

await client.send(
  new CompleteMultipartUploadCommand({
    Bucket: 'my-bucket',
    Key: 'video/raw.mov',
    UploadId,
    MultipartUpload: { Parts: [{ PartNumber: 1, ETag: etagFromPartResponse }] },
  })
)

Engade unha regra de ciclo de vida que cancele as subidas multipart incompletas despois duns días, ou as partes abandonadas seguirán ocupando almacenamento facturable.

URL presinadas en R2, B2, Spaces e Wasabi

Os provedores que implementan a API S3 con SigV4 aceptan o mesmo código: só cambias o endpoint, a rexión e as chaves. Os detalles varían, así que revisa a documentación do provedor:

  • Cloudflare R2 admite GET, HEAD, PUT e DELETE presinados, cunha caducidade de 1 segundo a 7 días. Non admite POST presinado, e as URL presinadas só funcionan no dominio da API S3 (<ACCOUNT_ID>.r2.cloudflarestorage.com), non en dominios personalizados. Usa region: 'auto'.
  • Backblaze B2 indica que a súa API compatible con S3 admite URL presinadas xeradas coa AWS CLI ou cos SDK.
  • DigitalOcean Spaces e Wasabi funcionan coas mesmas chamadas do SDK apuntadas aos seus endpoints.

Para os prezos de cada provedor, mira a comparativa de S3, R2, Spaces, Wasabi e B2.

Erros comúns coas URL presinadas e como solucionalos

ErroCausa habitualSolución
SignatureDoesNotMatchO método, as cabeceiras ou a query difiren do asinado; reloxo desincronizadoEnvía o mesmo Content-Type e as mesmas cabeceiras; sincroniza o reloxo con NTP
AccessDenied “Request has expired”A URL pasou de X-Amz-Date + X-Amz-ExpiresXera unha URL nova; reduce o tempo entre a sinatura e o uso
A URL caduca antes do pedidoAsinada con credenciais temporais (rol, EC2, ECS, Lambda)Asina con credenciais que duren tanto como teña que durar a ligazón
ExpiredTokenA sesión STS detrás de X-Amz-Security-Token rematouRenova as credenciais antes de asinar
403 AccessDenied nunha URL recenteQuen asina non ten s3:GetObject ou s3:PutObject, ou unha política o denegaConcede o permiso a quen asina; revisa as políticas do bucket
AuthorizationQueryParametersErrorCaducidade superior a 604.800 segundos ou ámbito da credencial mal formadoMantén X-Amz-Expires en 7 días ou menos; asina na rexión do bucket
O navegador bloquea por CORSNon hai regra de CORS para a túa orixe e o teu método no bucketEngade unha regra de CORS coa túa orixe, GET/PUT e o ETag exposto
AccessDenied con HeadersNotSignedAsinouse Range pero enviouse If-Range sen asinarEngade If-Range ás cabeceiras asinadas

Os proxies corporativos que reescriben cabeceiras ou query strings tamén rompen as sinaturas. Se unha URL funciona desde unha rede e non desde outra, proba sen o proxy.

Boas prácticas de seguridade

AWS chama ás URL presinadas bearer tokens e recomenda protexelas. Na práctica:

  • Mantenas curtas. Minutos para subidas e ficheiros sensibles, horas para descargas normais. Xera unha nova cando alguén volva necesitar o ficheiro.
  • Asina cunha identidade restrinxida. Un usuario ou rol de IAM dedicado que só poida ler (ou só escribir) o prefixo en cuestión limita o dano se se filtra unha ligazón.
  • Engade barreiras na política do bucket. A condición s3:signatureAge rexeita as sinaturas máis antigas dunha idade dada, e aws:SourceIp restrinxe desde que redes se poden usar.
  • Non as rexistres. As URL presinadas completas nos logs da aplicación, nas analíticas ou no historial dun chat son credenciais que funcionan ata que caducan.
  • Ten en conta o que non teñen. Non hai contrasinal, nin contador de descargas, nin revogación por ligazón, así que pon unha aplicación diante cando necesites esas cousas.

Para o lado de compartir en detalle, con opcións que engaden eses controis, mira como compartir ficheiros de S3 sen facer público o bucket.

Como usa Nubbo as URL presinadas

Nubbo está construído sobre este patrón. Cando subes no explorador, o servidor asina un PUT (ou, desde 100 MB, unha subida multipart con tres partes en paralelo) e o teu navegador envía os bytes directamente ao teu bucket. As descargas dun só ficheiro usan URL GET presinadas válidas durante unha hora. As túas access keys gárdanse cifradas con AES-256-GCM e só se usan no servidor para asinar.

As ligazóns para compartir engaden o que lles falta ás URL presinadas sen máis: un contrasinal, unha data de caducidade e un límite de descargas opcionais que Nubbo comproba antes de emitir unha URL de curta duración, e podes desactivar unha ligazón cando queiras. As descargas ZIP de varios ficheiros ármanse no teu navegador, que descarga cada ficheiro directamente do teu bucket: Nubbo nunca ve o contido. Unha única excepción, para ser claros: as miniaturas escríbense no teu propio bucket en .nubbo-thumbs/. Funciona igual en Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 e endpoints públicos compatibles con S3.

Preguntas frecuentes

Que é unha URL presinada en S3?

É unha ligazón HTTPS normal a un obxecto dun bucket privado cunha sinatura engadida á query string. Quen teña a ligazón pode facer a única operación para a que se asinou, normalmente unha descarga (GET) ou unha subida (PUT), ata que caduque. O bucket segue sendo privado e quen usa a ligazón non necesita credenciais de AWS.

Cal é a caducidade máxima dunha URL presinada de S3?

Sete días (604.800 segundos) cando asinas con Signature Version 4 e chaves de longa duración dun usuario de IAM. Unha URL asinada con credenciais temporais, como un rol asumido ou un perfil de instancia EC2, deixa de funcionar cando caducan esas credenciais, aínda que pedises máis tempo. Na consola de S3 podes escoller entre 1 minuto e 12 horas.

Que significan X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders e X-Amz-Signature?

Son os parámetros de autenticación de SigV4. Algorithm é o método de sinatura (AWS4-HMAC-SHA256), Credential é o ID da túa access key máis o ámbito de data, rexión e servizo, Date é cando se asinou a URL, Expires é cantos segundos segue sendo válida, SignedHeaders enumera as cabeceiras cubertas pola sinatura e Signature é a propia sinatura. X-Amz-Security-Token só aparece cando a URL se asinou con credenciais temporais.

Podo revogar unha URL presinada de S3 antes de que caduque?

Non unha por unha. Unha URL presinada só deixa de funcionar antes de tempo cando se revogan, borran ou desactivan as credenciais que a asinaron, ou cando unha política do bucket denega a petición. Se necesitas cancelar ligazóns concretas, pon diante unha aplicación que só entregue URL de curta duración despois das súas propias comprobacións.

As URL presinadas de S3 son seguras?

A sinatura non se pode falsificar nin alterar, e o bucket segue sendo privado. Pero AWS describe as URL presinadas como bearer tokens: calquera que consiga a ligazón pode usala, tantas veces como queira, ata que caduque. Mantén caducidades curtas, usa HTTPS e evita publicar as ligazóns onde se poidan copiar.

As URL presinadas funcionan con Cloudflare R2, Backblaze B2 ou Wasabi?

Os provedores que implementan a API S3 con Signature Version 4 adoitan admitilas, e usas as mesmas chamadas do SDK co endpoint do provedor. Cloudflare R2, por exemplo, admite GET, HEAD, PUT e DELETE presinados cunha caducidade de 1 segundo a 7 días, pero non subidas por formulario POST nin dominios personalizados.

Por que a miña URL presinada devolve SignatureDoesNotMatch?

AWS indica tres causas habituais: un reloxo do ordenador desincronizado, un proxy que cambia as cabeceiras ou a query string, e unha petición cuxo método, cabeceiras ou parámetros difiren dos que se asinaron. Nas subidas, o culpable habitual é enviar un Content-Type distinto do que se usou ao xerar a URL.

Fontes

Os prezos e límites saen das páxinas oficiais de abaixo. Os provedores cámbianos, así que compróbaos antes de decidir.