Membuat S3 Presigned URL: Akses Sementara ke File Private dengan AWS SDK

Ketika kamu perlu memberikan akses sementara ke file private di S3 tanpa mengekspos kredensial AWS atau mengubah bucket policy, presigned URL adalah solusi yang tepat. Skenario ini muncul terus-menerus di production — misalnya user ingin mengunduh invoice PDF mereka, atau aplikasi mobile perlu streaming video dari bucket private selama sesi aktif.

TL;DR: Membuat S3 Presigned URL

AspekDetail
TujuanBerikan akses unduh sementara ke objek S3 private
ExpirationDapat dikonfigurasi — contoh ini menggunakan 1 jam (3600 detik)
SDK yang DigunakanAWS SDK for Python (Boto3) dan JavaScript (v3)
IAM yang Diperlukans3:GetObject pada objek target
Batasan PentingURL ditandatangani dengan kredensial pemanggil — jika kredensial kedaluwarsa sebelum URL, URL ikut tidak valid

Bagaimana S3 Presigned URL Bekerja

Presigned URL bukan mekanisme autentikasi terpisah — ini adalah URL biasa yang menyertakan signature kriptografis dari kredensial AWS kamu sebagai bagian dari query string. Ketika S3 menerima request ke URL tersebut, S3 memvalidasi signature menggunakan Signature Version 4 (SigV4), memeriksa apakah URL belum kedaluwarsa, lalu melayani objek seolah-olah request datang langsung dari identity yang menandatanganinya.

Implikasinya: jika IAM role yang digunakan untuk membuat presigned URL di-revoke atau session token-nya kedaluwarsa sebelum presigned URL digunakan, URL tersebut akan langsung gagal meskipun expiration time belum tercapai. Ini sering menjadi sumber kebingungan di production ketika menggunakan temporary credentials dari IAM role (misalnya EC2 instance profile atau Lambda execution role).

sequenceDiagram participant App as Aplikasi Server participant SDK as AWS SDK participant Client as Client (Browser/Mobile) participant S3 as Amazon S3 App->>SDK: generate_presigned_url(bucket, key, expiry=3600) SDK-->>App: URL dengan SigV4 signature Note over SDK,App: Tidak ada request ke S3 di sini App-->>Client: Kirim presigned URL Client->>S3: HTTP GET presigned URL S3->>S3: Validasi signature & expiration S3-->>Client: 200 OK + objek file
  1. Aplikasi server memanggil SDK untuk menghasilkan presigned URL menggunakan kredensial IAM yang aktif.
  2. SDK menghitung SigV4 signature berdasarkan bucket, key, expiration, dan kredensial — tidak ada request ke S3 pada tahap ini.
  3. Server mengembalikan URL ke client (browser/mobile app).
  4. Client melakukan HTTP GET langsung ke S3 menggunakan URL tersebut — tanpa melewati server kamu.
  5. S3 memvalidasi signature dan expiration, lalu mengembalikan objek jika valid.

Prasyarat IAM untuk Membuat S3 Presigned URL

Identity yang memanggil operasi presigned URL generation harus memiliki izin s3:GetObject pada objek target. Tanpa izin ini, URL yang dihasilkan akan tetap menghasilkan 403 Access Denied saat digunakan — SDK tidak memvalidasi izin saat URL dibuat, hanya saat URL digunakan oleh S3.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowGetObjectForPresignedURL",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::nama-bucket-kamu/*"
    }
  ]
}
Analogi: Presigned URL seperti tiket konser yang sudah dicetak — tiket itu valid selama konser belum mulai (belum expired), tapi jika penerbit tiket (kredensial IAM) dicabut izinnya sebelum kamu masuk, tiket itu tidak akan diterima di pintu masuk (S3).

Membuat S3 Presigned URL dengan Boto3 (Python)

Boto3 menyediakan method generate_presigned_url langsung dari S3 client. Expiration diatur dalam satuan detik — untuk 1 jam, gunakan nilai 3600.

import boto3
from botocore.exceptions import ClientError

def generate_presigned_url(bucket_name, object_key, expiration=3600):
    """
    Menghasilkan presigned URL untuk mengunduh objek S3 private.

    :param bucket_name: Nama S3 bucket
    :param object_key: Key objek di dalam bucket
    :param expiration: Durasi validitas URL dalam detik (default: 3600 = 1 jam)
    :return: Presigned URL sebagai string, atau None jika gagal
    """
    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
        )
        return url
    except ClientError as e:
        print(f'Error generating presigned URL: {e}')
        return None

# Contoh penggunaan
if __name__ == '__main__':
    url = generate_presigned_url(
        bucket_name='nama-bucket-kamu',
        object_key='dokumen/invoice-2024-001.pdf'
    )
    if url:
        print(f'Presigned URL (valid 1 jam):\n{url}')

Perhatikan bahwa region_name harus sesuai dengan region bucket. Jika client dibuat tanpa region yang tepat, URL yang dihasilkan bisa menghasilkan redirect atau error pada bucket di region tertentu.

Membuat S3 Presigned URL dengan AWS SDK JavaScript v3

Di SDK v3, presigned URL dibuat menggunakan package terpisah @aws-sdk/s3-request-presigner bersama dengan GetObjectCommand dari @aws-sdk/client-s3.

🔽 Klik untuk melihat kode JavaScript (Node.js)
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 generatePresignedUrl(bucketName, objectKey, expiresInSeconds = 3600) {
  const command = new GetObjectCommand({
    Bucket: bucketName,
    Key: objectKey,
  });

  try {
    const url = await getSignedUrl(s3Client, command, {
      expiresIn: expiresInSeconds,
    });
    return url;
  } catch (error) {
    console.error('Error generating presigned URL:', error);
    throw error;
  }
}

// Contoh penggunaan
generatePresignedUrl('nama-bucket-kamu', 'dokumen/invoice-2024-001.pdf')
  .then(url => console.log('Presigned URL:', url))
  .catch(err => console.error(err));

Membuat S3 Presigned URL via AWS CLI

Untuk kebutuhan testing atau scripting cepat, AWS CLI juga mendukung pembuatan presigned URL. Perintah berikut menghasilkan URL dengan expiration 3600 detik (1 jam):

aws s3 presign s3://nama-bucket-kamu/dokumen/invoice-2024-001.pdf \
  --expires-in 3600 \
  --region us-east-1

Output-nya adalah URL lengkap yang bisa langsung digunakan di browser atau di-curl. Berguna untuk memverifikasi bahwa konfigurasi IAM dan bucket sudah benar sebelum mengintegrasikan ke aplikasi.

Diagnosis: URL Menghasilkan 403 Meski Baru Dibuat

Ini adalah pola kegagalan yang paling sering ditemui di production, dan hampir selalu salah didiagnosis.

Gejala: Presigned URL berhasil dibuat oleh SDK tanpa error, tapi ketika client mencoba menggunakannya, S3 mengembalikan 403 Access Denied.

Diagnosis awal yang salah: Kebanyakan engineer langsung memeriksa bucket policy atau Block Public Access settings — padahal itu bukan masalahnya. SDK tidak memvalidasi izin saat URL dibuat.

Penyebab sebenarnya yang paling umum:

  1. Kredensial temporary sudah expired: Jika server kamu menggunakan IAM role (Lambda, EC2, ECS), session token bisa kedaluwarsa sebelum presigned URL digunakan. Verifikasi dengan memeriksa expiration dari credentials yang aktif.
    aws sts get-caller-identity
    # Untuk melihat expiration session token pada temporary credentials:
    aws sts get-session-token --query 'Credentials.Expiration'
  2. Identity pembuat URL tidak punya izin s3:GetObject: Cek izin efektif identity yang membuat URL.
    aws iam simulate-principal-policy \
      --policy-source-arn arn:aws:iam::123456789012:role/NamaRoleKamu \
      --action-names s3:GetObject \
      --resource-arns arn:aws:s3:::nama-bucket-kamu/dokumen/invoice-2024-001.pdf
  3. S3 Block Public Access tidak relevan di sini — Block Public Access hanya memblokir akses berbasis ACL publik dan bucket policy publik, bukan presigned URL yang menggunakan autentikasi SigV4.

Satu hal yang tidak intuitif: presigned URL yang dibuat oleh Lambda function bisa valid selama maksimal durasi session role Lambda tersebut, bukan hanya nilai ExpiresIn yang kamu set. Jika kamu set ExpiresIn=86400 (24 jam) tapi session role Lambda hanya berlaku 1 jam, URL akan gagal setelah 1 jam — bukan 24 jam.

flowchart TD A[S3 Menerima Request
dengan Presigned URL] --> B{URL sudah
melewati ExpiresIn?} B -- Ya --> C[403 Access Denied
Request Expired] B -- Tidak --> D{SigV4 Signature
valid?} D -- Tidak --> E[403 Access Denied
Invalid Signature] D -- Ya --> F{Session credentials
masih aktif?} F -- Tidak --> G[403 Access Denied
Credentials Expired] F -- Ya --> H[200 OK
Objek dikembalikan]
  1. S3 menerima request dengan presigned URL.
  2. S3 pertama-tama memeriksa apakah URL sudah melewati ExpiresIn — jika ya, langsung 403.
  3. Jika belum expired, S3 memvalidasi SigV4 signature menggunakan kredensial yang tertanam di URL.
  4. Jika signature valid tapi kredensial underlying sudah di-revoke atau session expired, hasilnya tetap 403.
  5. Jika semua valid, S3 mengembalikan objek.

Praktik Terbaik Operasional untuk S3 Presigned URL

  • Gunakan expiration sesingkat mungkin yang masih fungsional untuk use case kamu. URL dengan expiration panjang yang bocor ke log atau third-party service bisa dieksploitasi.
  • Jangan log presigned URL secara lengkap di application log — perlakukan seperti secret. Log hanya metadata seperti bucket, key, dan waktu pembuatan.
  • Untuk file besar atau unduhan yang sering diulang, pertimbangkan CloudFront dengan signed URL atau signed cookies sebagai alternatif yang lebih scalable.
  • Jika menggunakan IAM role dengan temporary credentials, pastikan ExpiresIn presigned URL tidak melebihi sisa durasi session credentials yang digunakan untuk menandatanganinya.

Wrap-Up: S3 Presigned URL di Production

Membuat S3 presigned URL itu sederhana secara mekanis, tapi memahami model kepercayaannya — bahwa URL mewarisi validitas dari kredensial pembuatnya — adalah yang membedakan implementasi yang robust dari yang rapuh. Untuk langkah selanjutnya, pelajari dokumentasi resmi AWS tentang Sharing objects with presigned URLs dan pertimbangkan apakah use case kamu lebih cocok menggunakan CloudFront signed URL untuk distribusi konten skala besar.

Glosarium

IstilahPenjelasan
Presigned URLURL S3 yang menyertakan signature kriptografis sehingga siapa pun yang memilikinya bisa mengakses objek tanpa kredensial AWS sendiri
SigV4 (Signature Version 4)Protokol penandatanganan request AWS yang digunakan untuk mengautentikasi request ke layanan AWS termasuk S3
Temporary CredentialsKredensial AWS (access key + secret + session token) yang dikeluarkan oleh STS dengan durasi terbatas, biasanya digunakan oleh IAM role
s3:GetObjectIzin IAM yang diperlukan untuk membaca/mengunduh objek dari S3 bucket
ExpiresInParameter SDK yang menentukan berapa lama (dalam detik) presigned URL valid sejak dibuat

Related Posts

Komentar

Postingan populer dari blog ini

EC2 Tidak Bisa Akses Internet di Custom VPC: Cara Pasang Internet Gateway dan Update Route Table