En el servicio de S3 de AWS, todos los objetos que tengamos dispuestos ahí son privados por omisión. Sin embargo, hay ocasiones en que el dueño del objeto puede compartirlo con otros a traves de un URL prefirmado, usando sus propias credenciales de seguridad, para autorizar un permiso de descarga del objeto por una cantidad de tiempo limitada.

Vamos a ilustrar todo el proceso por medio de un microservicio en Quarkus. Este se encarga de recibir como entrada el bucket donde debemos subir el archivo -una imágen en este ejemplo-, el nombre del archivo y su contenido.

Primeramente, para subir el archivo al bucket usariamos un código similar al siguiente:

byte[] decodedBytes = Base64.getDecoder().decode(data.getPayload());
InputStream fis = new ByteArrayInputStream(decodedBytes);
TransferManager transferManager = TransferManagerBuilder.standard().withS3Client(s3Client).build();

try {
    ObjectMetadata metadata = new ObjectMetadata();
    metadata.setContentLength(decodedBytes.length);
    metadata.setContentType("image/jpeg");
    metadata.addUserMetadata("x-amz-meta-title", data.getKeyName());

    Upload upload = transferManager.upload(data.getBucketName(), data.getKeyName(),
            fis, metadata);
    upload.waitForCompletion();

} catch (AmazonServiceException | InterruptedException e) {
    LOG.log(Level.SEVERE, "Error al subir el archivo a S3", e);
} finally {
    transferManager.shutdownNow();
}

Hasta este punto, sólo hemos tomado un archivo que viene como entrada al servicio y lo hemos subido al bucket indicado con el nombre que se recibe.

La parte interesante es como generar ese URL prefirmado; y es de la siguiente forma:

// Generamos el URL
GeneratePresignedUrlRequest generatePresignedUrlRequest =
    new GeneratePresignedUrlRequest(data.getBucketName(), data.getKeyName())
            .withMethod(HttpMethod.GET)
            .withExpiration(expiration);
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

Nótese que debemos brindar el nombre del bucket, el objeto, el método por el cual será accesado (un GET en este caso) y hasta que momento estará vigente ese URL.

Es importante tomar en cuenta que dependiendo de las credenciales que se usaran para generar el URL, se tienen ciertas restricciones:

  • IAM instance profile: hasta 6 horas.
  • AWS Security Token Service: hasta 36 horas.
  • IAM User: hasta 7 días usando AWS Signature Versión 4.
  • Temporary Token: el URL expira cuando el token expire, sin importar si indicamos un tiempo de expiración posterior.

Otro punto a considerar es que cualquiera que tenga acceso a este URL, podrá descargar el objeto que compartimos; es para todos los efectos un token al portador. Si queremos limitar aún más el acceso, podemos establecer una politica de IAM que establezca que se puede acceder únicamente desde cierta IP o de ciertos bloques.

Asimismo, tal y como podemos descargar un archivo por medio de un URL prefirmado, también se puede usar para subir un archivo a un bucket de S3.

Conclusión

Espero que este artículo les haya sido de utilidad y consideren los URL prefirmados como opciones para compartir recursos de manera segura en AWS.