Hal-hal tentang S3 yang Anda Mungkin Berharap Tidak Perlu Tahu
(blog.plerion.com)- Dalam risiko kebocoran data AWS, akses tidak sah ke bucket S3 terus berulang, dan desain API lama serta perilaku pengecualian membuat penilaian sederhana “publik/privat” menjadi sulit
- Banyak operasi S3 tidak dipanggil melalui endpoint AWS umum, melainkan melalui URL bucket itu sendiri, dan pada kebijakan bucket yang keliru, operasi berbahaya dapat dilakukan bahkan dengan permintaan
curltanpa autentikasi - Sulit menganggap aman hanya dengan memblokir
s3:ListBucket; jalur sepertiListBucketVersions,ListMultipartUploads, danfetch-ownerdapat mengungkap key objek dan pengenal akun - Pengunggah dapat memengaruhi atribut objek seperti storage class, tag, Object Lock, dan beberapa header terkait redirect, sehingga diperlukan kontrol tambahan seperti kondisi IAM dan kebijakan lifecycle
- Jika hanya melihat ACL dan pengaturan block public access lalu menilai bucket sebagai privat, ada jalur yang bisa terlewat; pengguna internet dapat mengakses objek S3 melalui distribusi CloudFront atau Cognito identity pool
Desain API S3 lama dan panggilan anonim
- S3 adalah salah satu layanan awal AWS sehingga tangguh dan telah diuji dengan baik, tetapi karena jejak dari masa sebelum pola desain terstandardisasi, ia memiliki bentuk API yang berbeda dari layanan AWS lain
- Sebagian API S3 menggunakan endpoint umum seperti
s3.us-east-2.amazonaws.com, tetapi banyak operasi harus diminta langsung ke URL bucket target- Contoh melihat daftar bucket berbentuk
GET /denganHost: [bucketname].s3.amazonaws.com - Melihat tag bucket juga mengirim
GET /?taggingke host bucket tersebut
- Contoh melihat daftar bucket berbentuk
- Banyak layanan AWS seperti EC2 atau DynamoDB umumnya menggunakan endpoint umum dan menyampaikan resource target melalui header HTTP atau parameter
- Bucket S3 mendukung akses publik maupun akses terautentikasi, sehingga API operation mana yang dapat dilakukan tanpa autentikasi tidak selalu jelas
- Jika contoh kebijakan bucket mengizinkan
Principal: "*"danAction: "s3:*"pada resource bucket, bucket bahkan dapat dihapus dengan permintaan tanpa autentikasi- Contoh permintaannya adalah
curl -X DELETE https://[bucketname].s3-ap-southeast-2.amazonaws.com
- Contoh permintaannya adalah
- Sebagian operasi tidak mendukung permintaan anonim, dan mengembalikan error seperti
s3:GetBucketOwnershipControls does not support Anonymous requests! - Permintaan API anonim dicatat di CloudTrail sebagai akun anonymous
- Jika permintaan tidak terautentikasi, tidak dapat diidentifikasi siapa yang menghapus bucket atau melihat pengaturan enkripsi maupun status logging
- Jalur seperti
/?logging,/?tagging, dan/?encryptionjuga dapat diuji di browser - Ada juga operasi seperti
GetObjectTorrentyang dokumentasinya masih ada tetapi tidak lagi dapat dijalankan
Memblokir ListBucket saja sulit mencegah terungkapnya key objek
- Untuk mengunduh objek S3, diperlukan key masing-masing objek, dan key digunakan seperti path file
- Permintaan
GETke root bucket dapat mengembalikan isi bucket dalam kondisi tertentu, sehingga menolaks3:ListBuckettampak seperti pertahanan yang umum - Meski menggunakan ACL
public-readbersama kebijakan penolakans3:ListBucket, masih ada jalur untuk mendapatkan key objekGET /?versions, yaitus3:ListBucketVersions, mengembalikan metadata versi objek di dalam bucketGET /?uploads, yaitus3:ListMultipartUploads, mengembalikan daftar multipart upload yang sedang berlangsung
- Dokumentasi
HeadBucketmemuat keterangan tentang memeriksa keberadaan bucket dan hak akses, tetapi pada praktiknya ia memeriksa apakah ada izin untuk menjalankan operasiListBucket - Jika hanya memverifikasi apakah
ListBucketditolak, kemungkinan terungkapnya key objek S3 bisa terlewat
Biaya dan paparan multipart upload yang belum selesai
- Multipart upload dimulai dengan
create-multipart-upload, lalu part diunggah denganupload-part - Multipart upload yang belum selesai tidak mudah dilihat di konsol web, dan dapat diperiksa dengan
/?uploadsatauaws s3api list-multipart-uploads --bucket [bucket-name] - Jika permintaan penyelesaian tidak berhasil dikirim, Amazon S3 tidak merakit part dan tidak membuat objek
- Part yang telah diunggah tetap berada di akun sampai multipart upload selesai atau dibatalkan
- Part yang disimpan menimbulkan biaya storage S3
- Cara mengunduh part objek sebelum selesai tidak ditemukan, tetapi penghapusan dimungkinkan
- AWS menyarankan penerapan aturan lifecycle yang menghapus upload yang belum selesai setelah sejumlah hari tertentu
- Jika multipart upload yang belum selesai didaftarkan dengan
/?uploads, ARN principal yang memulai upload akan dikembalikan- Jika pengenal seperti account ID dan ARN dianggap tidak sensitif, ini mungkin bukan masalah
- Jika tidak ingin pengenal yang berguna bagi penyerang terbuka, ini dapat dianggap sebagai paparan
ACL dan verifikasi akun berbasis email
- Dokumentasi S3 ACL masih menyimpan jejak masa ketika akun AWS diidentifikasi dengan email root user
- Operasi
PutBucketACLdapat menetapkan grantee dengan alamat email- Menggunakan
Type: AmazonCustomerByEmaildanEmailAddress
- Menggunakan
- Jika tidak ada akun AWS yang terhubung ke email yang ditentukan, error
UnresolvableGrantByEmailAddressterjadi- Pesan error berbunyi bahwa “alamat email yang diberikan tidak cocok dengan akun mana pun dalam catatan”
- Karena perilaku ini, dapat diperiksa apakah alamat email tertentu memiliki akun AWS terdaftar
Storage class dan metadata objek yang dapat dipilih pengunggah
- Storage class S3 diterapkan pada objek, bukan bucket
- Tidak ada pengaturan untuk mengunci storage class yang diinginkan pada level bucket, dan pihak yang mengunggah dapat menentukan storage class objek
- Contohnya
aws s3 cp "my.txt" "s3://mybucket/myobject.txt" --storage-class [CLASS]
- Contohnya
- Dalam daftar yang telah ditentukan, pengunggah dapat memengaruhi biaya penyimpanan dan akses per GB yang ditanggung pemilik bucket
- Dengan menggunakan condition key
s3:x-amz-storage-classdalam kebijakan IAM, storage class yang diizinkan dapat dibatasi- Contoh kebijakan hanya mengizinkan
STANDARDuntuks3:PutObject
- Contoh kebijakan hanya mengizinkan
- Jika kebijakan lifecycle dikonfigurasi, semua objek dapat dialihkan ke storage class tertentu setelah jangka waktu tertentu
- Pada upload yang menggunakan pre-signed URL, AWS Signature Version 4 mewajibkan tanda tangan untuk semua header yang diawali
X-Amz-- Storage class ditentukan dengan header
x-amz-storage-class - Jika aplikasi tidak diimplementasikan dengan sangat buruk, tidak ada cara langsung yang jelas untuk memanipulasinya
- Storage class ditentukan dengan header
Tag, Object Lock, dan redirect juga berada dalam pengaruh pengunggah
- Banyak atribut terkait objek S3 dikendalikan oleh pengunggah
- Tag objek dapat ditentukan saat upload
- Contohnya
--tagging "AllYourTags=AreBelong&To=Us"
- Contohnya
- Sistem yang menjalankan otomasi berdasarkan nilai tag dapat dipengaruhi oleh nilai tag yang dibuat pengunggah
- Object Lock dapat menetapkan retensi objek dan legal hold jika object locking diaktifkan pada bucket
- Contoh perintah menggunakan
--object-lock-retain-until-date "2099-01-01T00:00:00+0000",--object-lock-legal-hold-status "ON", dan--object-lock-mode "COMPLIANCE"
- Contoh perintah menggunakan
- Pada bucket yang mengaktifkan static website hosting, open redirect dapat dilakukan menggunakan pengaturan file yang diunggah
- Daftar lengkap header yang didukung
PutObjectjuga perlu diperhatikan- Ada batasan pada pre-signed URL
- Dalam konfigurasi yang bergantung pada Cognito identity dan kebijakan IAM, permintaan dapat ditandatangani dalam konteks Cognito yang terautentikasi
Paparan pemilik bucket dan pengenal akun
- Untuk memeriksa apakah account ID tertentu adalah pemilik bucket yang dapat diakses, header
x-amz-expected-bucket-ownerdapat dimasukkan ke permintaanListBucket- Jika account ID yang salah dimasukkan,
AccessDenieddikembalikan - Jika account ID benar dan pemanggil memiliki izin
ListBucket, respons normal dikembalikan
- Jika account ID yang salah dimasukkan,
- Jika parameter
fetch-owner=truepada APIListBucketdigunakan, setiap key dalam respons menyertakan elemen Owner IDdi dalamOwneradalah string heksadesimal 64 karakter yang disebut canonical user ID dalam dokumentasi AWS- Ini adalah bentuk tersamarkan dari AWS account ID
- Jika canonical user ID dimasukkan sebagai
CanonicalUserpadaPrincipaldalam kebijakan IAM, disimpan, lalu halaman disegarkan, nilainya akan diinterpretasikan sebagai AWS account ID ListBucketVersionsdanListMultipartUploadsjuga berperilaku serupa tanpafetch-owner
Key objek S3 terlihat seperti nama file, tetapi berperilaku berbeda
- Key objek S3 peka huruf besar-kecil
- Meski namanya terlihat sama, jika kapitalisasinya berbeda, beberapa objek dapat diunggah
- Masalah dapat terjadi jika aplikasi memperlakukan key objek S3 seperti nama file yang tidak peka huruf besar-kecil
- Contoh aplikasi menyimpan kata sandi pengguna dalam file S3 dan menggunakan nama pengguna sebagai nama file
- Saat pendaftaran, aplikasi hanya memeriksa keberadaan file, dan saat mengganti kata sandi, nama pengguna diubah menjadi huruf kecil lalu ditulis ke file
- Meski
jeffsudah ada, pendaftaranJEFFdimungkinkan, dan penggunaJEFFdapat menimpa file milikjeffmelalui perubahan kata sandi
- Key objek S3 dapat menggunakan karakter UTF-8 apa pun
- Karakter tertentu dapat menimbulkan masalah pada sebagian aplikasi dan protokol
- Spasi, garis miring, karakter persen, dan lainnya juga valid dalam key objek
Meski terlihat seperti “bucket privat”, jalur akses bisa tetap ada
- Meski ACL dimatikan, resource policy diatur secara sempit, dan block public access diaktifkan, bucket bisa tetap dapat diakses publik
- Jalur paling umum adalah distribusi Amazon CloudFront
- Jika CDN ditempatkan di depan bucket S3, biasanya ada niat untuk mendistribusikan konten ke internet
- Alat keamanan dapat menilai bucket tidak publik jika resource policy bucket dibatasi ke CloudFront
- Dalam contoh,
get-bucket-policy-statusmengembalikanIsPublic: false- Jika permintaan dikirim langsung ke bucket,
AccessDenieddikembalikan - Jika permintaan yang sama dikirim ke domain distribusi CloudFront, isi objek dikembalikan
- Jika permintaan dikirim langsung ke bucket,
- Cognito identity pool juga dapat mengekspos bucket yang memiliki resource policy terbatas
- Setelah login berhasil, Cognito menyediakan kredensial AWS sementara untuk peran yang telah dikonfigurasi sebelumnya
- Jika peran tersebut memiliki izin
s3:ListBucketdans3:GetObject, pengguna dapat memanggil API S3
- Ada dua konfigurasi Cognito yang dapat termasuk akses publik
- Self-registration: jika pengguna internet dapat mendaftar dan login ke aplikasi, ini pada praktiknya menjadi akses publik
- Guest access: memberikan pengenal unik dan kredensial AWS kepada pengguna yang tidak terautentikasi
- Contoh guest access adalah alur menerima
IdentityIddenganget-id, menerima kredensial sementara denganget-credentials-for-identity, lalu menjalankanaws s3 lsdengan profil tersebut - CloudFront dan Cognito identity pool memang sering digunakan di internet, tetapi merupakan jalur akses publik yang jarang ditampilkan oleh alat keamanan
1 komentar
Opini Hacker News
Ada banyak poin menarik, tetapi sulit untuk setuju bahwa sistem berkas yang membedakan huruf besar-kecil adalah sesuatu yang patut dikeluhkan
Menurut saya memang seharusnya begitu, dan justru menyebalkan bahwa macOS tidak melakukannya
Pembedaan huruf besar-kecil pada nama berkas bisa terasa mengejutkan bahkan bagi pengguna nonteknis. Kalau seseorang bilang mengirim “Book Draft 1.docx” tetapi di kotak masuk ada “Book draft 1.docx”, biasanya orang tidak akan bilang, “Sepertinya Anda mengirim berkas yang berbeda?”
Dalam tulisan pun huruf besar-kecil biasanya tidak mengubah makna. “Hi, how are you?” dan “hi, how are you?” artinya sama, dan huruf besar mengubah makna hanya saat membedakan nama diri dan kata benda umum, yang jarang menjadi perhatian dalam nama berkas
Terlepas dari selera pribadi, sulit dipahami kalau developer atau administrator sistem terkejut atau kesal karena sistem berkas membedakan huruf besar-kecil. Jika perlu, developer bisa mengabstraksikannya untuk pengguna akhir, seperti pada hasil pencarian
Ini juga bukan seperti coding, yang pemaksaan gaya kode bisa membantu keterbacaan. Dalam pemrograman pun, sampai IDE menjadi cukup pintar untuk menangkap typo pada nama variabel, hal ini sering menjadi sumber bug. Salah satu hal yang saya suka dari Pascal adalah, tidak seperti C, kita tidak perlu memikirkan huruf besar-kecil
Anda bisa menulis nama berkas dengan gaya yang diinginkan dan penulisannya dipertahankan, tetapi saat mencari atau memprosesnya, Anda tidak perlu mengingat gaya itu secara persis. Sebab pencarian tidak membedakan huruf besar-kecil
Pembedaan huruf besar-kecil tergolong mudah, dan yang kurang intuitif adalah bahwa path S3 itu palsu
S3 menerima unggahan “/builds/1/installer.exe” dan juga menampilkan daftar di dalam /builds, tetapi sebenarnya yang diunggah adalah satu key bernama '/builds/1/installer.exe' yang mengandung '/' dalam namanya
Jadi “/builds/1//installer.exe” dan “/builds//1/installer.exe” juga bisa diunggah dan merupakan berkas yang sepenuhnya berbeda. Itu hanya nama key; tidak ada direktori sungguhan
[1] https://docs.aws.amazon.com/AmazonS3/latest/userguide/direct...
Tahun ini sistem produksi kami juga terkena bug aneh dan baru ketemu setelah 5 orang turun tangan. Penyebabnya adalah objek yang namanya secara literal “/”, dan software mencoba memperlakukannya sebagai path, bukan sebagai berkas
Sulit memakai S3 atau layanan AWS lain dengan percaya diri. Tidak ada yang intuitif, terlalu banyak komponen bergerak, dan terlalu banyak dokumentasi yang harus dibaca
Bahkan setelah itu pun, seperti di artikel aslinya, Anda masih bisa tidak sengaja membuka semuanya ke publik. Saya lebih memilih layanan yang benar-benar sederhana seperti Hetzner Storage Boxes atau DigitalOcean Spaces
Baru-baru ini saya mengetahui bahwa jika mengunggah file video yang lebih besar dari beberapa MB lewat pipe, Location yang dikembalikan menghilangkan https://. Jadi pada setiap unggahan file, saya harus memeriksa apakah Location diawali https, dan menambahkannya jika tidak ada
Tentu saja, di issue GitHub klien S3 Node mereka bilang “Sepertinya bug DigitalOcean”, sedangkan di forum DigitalOcean mereka bilang “Sepertinya bug klien S3 Node”
Banyak fitur dan keanehan yang awalnya dirancang untuk membantu sebagian situasi khusus kini menjadi bagian dari protokol umum. Sepertinya itu terjadi karena secara bisnis mereka berusaha agar tidak ada siapa pun yang berpaling
Menghapus puluhan miliar objek juga harus dilakukan dengan hati-hati. Jika memanggil API penghapusan secara langsung, biayanya bisa mahal
Sebagai gantinya, Anda bisa mengatur aturan siklus hidup secara gratis dengan menetapkan waktu kedaluwarsa ke now untuk wildcard atau seluruh bucket. Dengan begitu, penagihan storage langsung berhenti dan AWS akan menangani penghapusannya sendiri
Ini juga menghindari server API S3 dihantam jumlah request per detik
Yang benar-benar menyebalkan adalah multipart upload yang gagal bisa tertinggal tanpa terlihat, dan kalau tidak secara eksplisit mengatur siklus hidup, Anda bahkan dikenai biaya storage
Saya kira huruf S pada “Simple” berarti sederhana
Usulan saya adalah part dari upload yang belum selesai hanya tersisa 24 jam sejak aktivitas terakhir, dan selama itu biaya storage juga tidak ditagihkan. ahenry@ menolaknya
Di sebuah server yang sangat tua, selama hampir 10 tahun ada skrip cron yang berjalan setiap malam untuk memulai multipart upload. Tujuannya mendorong backup ke bucket, tetapi bucket itu juga menyimpan konten unggahan pengguna, jadi wajar saja jika terlihat bertambah sedikit setiap hari
Skripnya dalam kondisi “tidak berfungsi”, jadi kami tidak bergantung pada data backup tersebut, file-nya tidak terlihat di S3, dan ukuran bucket bertambah stabil tetapi tidak berlebihan. Namun ketika kami mengeceknya musim semi ini, ternyata ada hampir 3TB multipart upload yang belum selesai tersimpan
Tentu saja saya tahu kisah ini penuh praktik buruk
Diskusi soal case-sensitive/case-insensitive umumnya terasa terlalu berpusat pada bahasa Inggris
Dengan kata lain, terutama di IT, diskusi terkait bahasa terlalu sering sangat berpusat pada bahasa Inggris
Sebagai orang yang bahasa ibunya bukan Inggris, menurut saya pemrograman sudah memiliki terlalu banyak konsep dan elemen. Lebih baik tidak menambah kompleksitas dengan mempertimbangkan 101 bahasa lain
Unicode dan zona waktu adalah contoh utama unsur yang mencoba mempertimbangkan lebih banyak bahasa dan budaya dalam pemrograman, tetapi hasilnya justru menjadi sumber penderitaan terbesar bagi semua orang, termasuk programmer non-Inggris
Saya tidak ingin menulis program dalam bahasa ibu saya. Apalagi jika imbalannya adalah harus mempertimbangkan semua bahasa utama saat memprogram. Tidak apa-apa jika diskusi IT berpusat pada bahasa Inggris. Keberagaman adalah kompleksitas, dan bahasa Inggris bukan bahasa milik seseorang, melainkan sekadar alat yang dipakai orang untuk berkomunikasi
Berkat bahasa bersama itu, saya bisa menyampaikan pikiran saya kepada banyak orang di India, Tiongkok, Jepang, Amerika Selatan, dan seterusnya. Begitu mereka memutuskan berbicara dalam bahasa Inggris, mereka juga memiliki bahasa Inggris. Tidak perlu membawa politik keberagaman ke IT; lebih baik tetap menempatkannya secara teknis
Dalam beberapa hal, kedua aksara suku kata itu terasa seperti alfabet huruf besar/huruf kecil
Ada beberapa hal lagi
Multipart upload tidak bisa dilakukan dari beberapa mesin yang memakai kredensial instans. Karena prinsipalnya berbeda, mereka tidak bisa mengakses multipart upload milik satu sama lain. Untuk merakit satu multipart upload dari beberapa mesin, Anda membutuhkan pengguna IAM sungguhan
Permintaan LIST bukan hanya lambat, tetapi juga sangat mahal jika dilakukan dalam jumlah besar. Ada workaround seperti “bucket inventory”, tetapi itu juga tidak nyaman maupun murah
Pembuatan bucket secara internal memakai DNS, sehingga tidak memiliki konsistensi read-after-write. Karena itu, kadang bucket tidak bisa diakses segera setelah dibuat, atau bucket yang baru dibuat tidak bisa dihapus sebelum menunggu cukup lama sampai perubahan terpropagasi. Lihat https://github.com/julik/talks/blob/master/euruko-2019-no-su...
Anda bisa membuat objek bernama “foo” dan objek bernama “foo/bar” secara bersamaan. Ini menghasilkan struktur di mana file menimpa direktori, sehingga data bucket tidak bisa dipindahkan ke struktur sistem file
S3 membedakan huruf besar-kecil, sehingga Anda bisa membuat objek yang tidak bisa dipindahkan ke struktur sistem file. Penyimpanan file Rails pernah rusak parah di macOS karena mengasumsikan penyimpanan yang case-sensitive, lalu diperbaiki agar selalu memakai identifier huruf kecil
Sebagian besar konfigurasi S3 mengizinkan GET tetapi tidak mengizinkan HEAD. Sepertinya ini cara untuk mencegah probing keberadaan objek, tetapi saya tidak yakin. Bagaimanapun, alur yang ramah cache untuk mengecek ukuran objek dengan permintaan HEAD tidak berfungsi, terutama pada pre-signed URL. Sebagai gantinya, Anda harus memakai GET dengan Range yang sangat kecil, misalnya hanya mengambil byte pertama
Jika Anda membuat banyak pre-signed URL, ada kemungkinan mempercepat pembuatannya 10–40 kali: https://github.com/WeTransfer/wt_s3_signer
Anda tetap membayar biaya penyimpanan untuk multipart upload yang belum selesai. Ini perlu sangat diwaspadai terutama jika arsitektur Anda memungkinkan pengguna memulai upload seperti ini. Ada pengaturan untuk menghapus otomatis multipart upload yang belum selesai setelah jangka waktu tertentu; aktifkan jika tidak ingin repot
Secara paradoks, S3 dulu revolusioner dan sampai sekarang tetap merupakan produk yang sangat bagus di banyak lapisan. Hanya saja, karena fiturnya banyak, jebakannya juga banyak
Di Elixir, saya membuat pipeline pascapemrosesan CSV streaming yang mengubah dan menyisipkan kolom menggunakan Stream.transform(https://hexdocs.pm/elixir/Stream.html#transform/3). Modul AWS dan CSV di Elixir memproses data streaming yang masuk, tetapi saya sedih karena jika total stream keluar kurang dari 5MiB, modul AWS memakai multipart upload sehingga S3 mengembalikan error
Ada masalah menarik lain yang saya dan rekan kerja analisis selama beberapa hari hingga terdiagnosis. S3 diam-diam membuang permintaan berikutnya setelah satu koneksi TCP mengirim 100 permintaan HTTP
https://github.com/aws/aws-sdk-go/issues/2825
Ini pola umum ketika Anda menginginkan keep-alive demi performa, tetapi ingin mencegah klien tetap tersambung terlalu lama hingga menciptakan hotspot pada load balancer
Ada juga fakta bahwa pada storage class standar, S3 memiliki latensi tinggi sehingga tidak cocok untuk serving web
Banyak orang mengira resource situs web seperti gambar atau font bisa langsung di-host dari S3, tetapi pengalaman pengguna bisa memburuk
“applications can achieve consistent small object latencies (and first-byte-out latencies for larger objects) of roughly 100–200 milliseconds.”
Sumber: https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimi...
Dengan CloudFront signed cookies, Anda juga bisa memberi pengguna tertentu akses CDN hanya ke konten miliknya di S3. Cukup keren
Ini meng-cache aset yang sering diakses untuk mengurangi latensi, dan juga bisa menurunkan biaya cukup banyak
Bahwa uploader yang menentukan aturan itu cukup ekstrem. Kalau sebuah situs web konfigurasinya longgar, apakah itu berarti pengguna dengan motivasi yang cukup bisa membuat konten pengguna diunggah ke Amazon Glacier, lalu nantinya disajikan dari sana?
https://docs.aws.amazon.com/service-authorization/latest/ref...
Khususnya, condition key ada di sini, dan Anda bisa melihat key untuk mengontrol akses berdasarkan storage class, tagging, dan sebagainya
https://docs.aws.amazon.com/service-authorization/latest/ref...