Subir imágenes de forma segura a un bucket de S3 sin exponer credenciales es una práctica esencial en arquitecturas serverless. Aquí se explica paso a paso cómo crear una Lambda que firma URLs, conectarla con API Gateway y configurar todo desde el archivo serverless.yml, incluyendo variables de ambiente, permisos IAM y validación de parámetros.
¿Cómo funciona la firma de URLs en S3?
El flujo es directo: una Lambda genera una URL firmada (signed URL) y se la entrega al cliente —un navegador u otra aplicación— para que este suba la imagen directamente al bucket de S3 sin pasar por el servidor. Esto reduce la carga en la infraestructura y mejora la seguridad.
El código de la Lambda importa el SDK de Amazon para crear un cliente de S3 [0:52]. Es importante notar que el SDK no se limita a DynamoDB; permite generar múltiples clientes para interactuar con distintos servicios del ecosistema AWS. Al instanciar el cliente, se especifica un signature version en versión cuatro [1:15], requisito indispensable para poder firmar URLs.
¿Qué parámetros necesita la URL firmada?
La función handler recibe el objeto event y extrae el parámetro filename desde queryStringParameters [1:40]. Este nombre de archivo es obligatorio porque S3 necesita saber con qué nombre se almacenará el objeto dentro del bucket.
La firma se genera con la función getSignedUrl del cliente de S3, donde se pasan tres parámetros clave [2:15]:
- key: la ruta compuesta por la carpeta
upload/ más el filename.
- bucket: el nombre del bucket, obtenido desde una variable de ambiente.
- expires: el tiempo de vida de la URL, fijado en 300 segundos.
El valor de putObject como primer argumento indica que la URL firmada servirá exclusivamente para almacenar objetos. Si alguien intenta usar la URL después de que expire, Amazon retorna un error 403, siguiendo las buenas prácticas de seguridad alineadas con el Well-Architected Framework [2:55].
¿Cómo configurar el serverless.yml para la Lambda?
Aquí reside lo más relevante del ejercicio. La configuración se aborda en tres bloques: variables de ambiente, definición de la función y permisos IAM.
¿Cómo se definen variables de ambiente en Serverless Framework?
Dentro del bloque provider se agrega el key environment [3:30]. Cada variable definida allí estará disponible en todas las Lambdas del proyecto. Para este caso se crea la variable BUCKET con el nombre exacto del bucket creado en la clase anterior.
yaml
provider:
environment:
BUCKET: nombre-del-bucket
¿Cómo se define la función Lambda y su evento HTTP?
La nueva función se llama signed_url. El handler apunta al archivo signedUrl/handler.signedUrl y no requiere especificar runtime porque se hereda del provider [4:25]. El evento es de tipo HTTP con método GET y ruta signed_url.
Para validar que el cliente envíe el filename, se usa la sección request.parameters.querystrings con la bandera true [5:10]. Así, API Gateway rechaza la petición si falta el parámetro, sin necesidad de lógica adicional en la Lambda.
yaml
functions:
signedUrl:
handler: signedUrl/handler.signedUrl
package:
include:
- signedUrl/handler.js
events:
- http:
path: signed_url
method: GET
request:
parameters:
querystrings:
filename: true
¿Cómo otorgar permisos de S3 al rol de la Lambda?
En el bloque iamRoleStatements se agrega un nuevo permiso con efecto Allow, acción s3:* y como recurso el ARN del bucket [5:50]. El ARN se encuentra en la consola de S3, dentro de las propiedades del bucket. Se añade /* al final para cubrir todos los objetos.
En el curso se usa s3:* por practicidad, pero la recomendación es aplicar el principio de mínimo privilegio y otorgar solo los permisos necesarios, como s3:PutObject [6:30].
¿Cómo probar la URL firmada con Postman?
Tras ejecutar sls deploy, el endpoint GET aparece en la salida del despliegue [7:05]. Al enviar la petición sin parámetros, API Gateway responde indicando que falta un parámetro requerido [7:30]. Al agregar ?filename=imagen_ejemplo.png, la Lambda retorna una URL extensa que contiene información de seguridad, expiración y destino.
Para subir la imagen se copia esa URL en un nuevo request de tipo PUT en Postman, se selecciona el body como binary y se adjunta el archivo [8:10]. La respuesta 200 confirma que la imagen quedó almacenada. Al revisar el bucket en la consola de S3, la carpeta upload/ contiene el archivo con el nombre especificado [8:40].
¿Has implementado URLs firmadas en tus proyectos? Comparte tu experiencia y las variaciones que has aplicado en los comentarios.