Menggunakan kondisi dalam Aturan Keamanan Firebase Cloud Storage

Panduan ini dikembangkan dari panduan mempelajari sintaksis inti bahasa Firebase Security Rules untuk menunjukkan cara menambahkan kondisi ke Firebase Security Rules untuk Cloud Storage.

Elemen penyusun utama Cloud Storage Security Rules adalah kondisi. Kondisi adalah ekspresi boolean yang menentukan apakah operasi tertentu diizinkan atau ditolak. Untuk aturan dasar, penggunaan literal true dan false sebagai kondisi berfungsi dengan sangat baik. Namun, bahasa Firebase Security Rules untuk Cloud Storage memberi Anda cara untuk menulis kondisi yang lebih kompleks yang dapat:

  • Memeriksa autentikasi pengguna
  • Memvalidasi data yang masuk

Autentikasi

Firebase Security Rules untuk Cloud Storage terintegrasi dengan Firebase Authentication untuk menyediakan autentikasi berbasis pengguna yang andal untuk Cloud Storage. Hal ini memungkinkan kontrol akses terperinci berdasarkan klaim token Firebase Authentication.

Saat pengguna terautentikasi membuat permintaan ke Cloud Storage, variabel request.auth akan diisi dengan uid (request.auth.uid) pengguna serta klaim JWT Firebase Authentication (request.auth.token).

Selain itu, saat menggunakan autentikasi kustom, klaim tambahan akan muncul di kolom request.auth.token.

Saat pengguna tanpa autentikasi membuat permintaan, variabel request.auth adalah null.

Dengan menggunakan data ini, ada beberapa cara umum menggunakan autentikasi untuk mengamankan file:

  • Publik: abaikan request.auth
  • Pribadi terautentikasi: pastikan request.auth bukan null
  • Pribadi pengguna: pastikan request.auth.uid sama dengan jalur uid
  • Pribadi grup: pastikan klaim token kustom cocok dengan klaim yang dipilih, atau baca metadata file untuk melihat apakah ada kolom metadata atau tidak

Publik

Setiap aturan yang tidak mempertimbangkan konteks request.auth dapat dianggap sebagai aturan public, karena tidak mempertimbangkan konteks autentikasi pengguna. Aturan ini dapat berguna untuk menampilkan data publik seperti aset game, file suara, atau konten statis lainnya.

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

Pribadi terautentikasi

Dalam kasus tertentu, Anda mungkin ingin agar data dapat dilihat oleh semua pengguna aplikasi yang terautentikasi, tetapi tidak oleh pengguna tanpa autentikasi. Karena variabel request.auth adalah null untuk semua pengguna tanpa autentikasi, Anda hanya perlu memastikan bahwa variabel request.auth ada untuk mengharuskan autentikasi:

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

Pengguna pribadi

Sejauh ini, kasus penggunaan request.auth yang paling umum adalah memberikan izin terperinci kepada pengguna individual untuk mengakses file mereka: mulai dari mengupload gambar profil hingga membaca dokumen pribadi.

Karena file di Cloud Storage memiliki "jalur" lengkap ke file, yang diperlukan untuk membuat file dikontrol oleh pengguna adalah bagian dari informasi identifikasi pengguna yang unik pada awalan nama file (seperti uid pengguna) yang dapat diperiksa saat aturan dievaluasi:

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth.uid == userId;
}

Grup pribadi

Kasus penggunaan umum lainnya adalah memberikan izin grup pada suatu objek, seperti mengizinkan beberapa anggota tim untuk berkolaborasi pada dokumen bersama. Ada beberapa pendekatan untuk melakukan hal ini:

  • Membuat token kustom Firebase Authentication yang berisi informasi tambahan tentang anggota grup (seperti ID grup)
  • Menyertakan informasi grup (seperti ID grup atau daftar uid yang diberi otorisasi) di metadata file

Setelah disimpan dalam token atau metadata file, data ini dapat direferensikan dari dalam aturan:

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

Evaluasi Permintaan

Upload, download, perubahan metadata, dan penghapusan dievaluasi menggunakan request dikirim ke Cloud Storage. Selain ID unik pengguna dan payload Firebase Authentication di objek request.auth seperti yang dideskripsikan di atas, variabel request berisi jalur file tempat permintaan akan dijalankan, waktu saat permintaan diterima, dan nilai resource baru jika permintaan tersebut adalah permintaan tulis.

Objek request juga berisi ID unik pengguna dan payload Firebase Authentication di objek request.auth yang akan dijelaskan lebih lanjut di bagian Keamanan Berbasis Pengguna dalam dokumentasi.

Daftar lengkap properti di objek request tersedia di bawah:

Properti Jenis Deskripsi
auth map<string, string> Saat pengguna login, memberikan uid, ID unik pengguna, dan token, peta klaim JWT Firebase Authentication. Jika pengguna tidak login, nilainya adalah null.
params map<string, string> Peta yang memuat parameter kueri permintaan.
path jalur path yang mewakili jalur tempat permintaan dijalankan.
resource map<string, string> Nilai resource baru, hanya ada pada permintaan write.
time timestamp Stempel waktu yang menunjukkan waktu server ketika permintaan dievaluasi.

Evaluasi Resource

Saat mengevaluasi aturan, Anda mungkin juga ingin mengevaluasi metadata dari file yang diupload, didownload, diubah, atau dihapus. Dengan begitu, Anda dapat membuat aturan yang rumit dan kuat untuk melakukan hal spesifik, seperti hanya mengizinkan upload file dengan jenis konten tertentu atau penghapusan file dengan ukuran lebih besar dari ukuran tertentu.

Firebase Security Rules untuk Cloud Storage memberikan metadata file di objek resource, yang berisi key-value pair dari metadata yang muncul pada objek Cloud Storage. Properti ini dapat diinspeksi pada permintaan read atau write untuk memastikan integritas data.

Pada permintaan write (seperti upload, pembaruan metadata, dan penghapusan), selain objek resource, yang berisi metadata file untuk file yang saat ini ada di jalur permintaan, Anda juga dapat menggunakan objek request.resource, yang berisi subset metadata file yang akan ditulis jika operasi tulis diizinkan. Anda dapat menggunakan kedua nilai tersebut untuk memastikan integritas data atau memberlakukan pembatasan aplikasi, seperti jenis atau ukuran file.

Daftar lengkap properti di objek resource tersedia di bawah:

Properti Jenis Deskripsi
name string Nama lengkap objek
bucket string Nama bucket yang ditempati objek ini.
generation int Google Cloud Storage object generation dari objek ini.
metageneration int Google Cloud Storage object metageneration dari objek ini.
size int Ukuran objek, dalam byte.
timeCreated timestamp Stempel waktu yang menunjukkan kapan objek dibuat.
updated timestamp Stempel waktu yang menunjukkan kapan objek terakhir diperbarui.
md5Hash string Hash MD5 untuk objek ini.
crc32c string Hash crc32c untuk objek ini.
etag string Etag yang terkait dengan objek ini.
contentDisposition string Disposisi konten yang terkait dengan objek ini.
contentEncoding string Encoding konten yang terkait dengan objek ini.
contentLanguage string Bahasa konten yang terkait dengan objek ini.
contentType string Jenis konten yang terkait dengan objek ini.
metadata map<string, string> Key-value pair untuk metadata kustom tambahan yang ditetapkan developer.

request.resource berisi semua properti di atas kecuali generation, metageneration, etag, timeCreated, dan updated.

Melakukan peningkatan dengan Cloud Firestore

Anda dapat mengakses dokumen di Cloud Firestore untuk mengevaluasi kriteria otorisasi lainnya.

Dengan menggunakan fungsi firestore.get() dan firestore.exists(), aturan keamanan Anda dapat mengevaluasi permintaan yang masuk berdasarkan dokumen di Cloud Firestore. Fungsi firestore.get() dan firestore.exists() sama-sama memerlukan jalur dokumen yang ditentukan secara lengkap. Ketika menggunakan variabel untuk membuat jalur bagi firestore.get() dan firestore.exists(), Anda harus meng-escape variabel secara eksplisit menggunakan sintaksis $(variable).

Pada contoh di bawah, kita melihat aturan yang membatasi akses baca ke file kepada pengguna yang merupakan anggota klub tertentu.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.id)).memberships
    }
  }
}
Pada contoh berikutnya, hanya teman pengguna yang dapat melihat fotonya.
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
    }
  }
}

Setelah membuat dan menyimpan Cloud Storage Security Rules pertama Anda yang menggunakan fungsi Cloud Firestore ini, Anda akan diminta di Firebase console atau Firebase CLI untuk mengaktifkan izin guna menghubungkan kedua produk.

Anda dapat menonaktifkan fitur ini dengan menghapus peran IAM, seperti yang dijelaskan dalam Mengelola dan men-deploy Firebase Security Rules.

Memvalidasi data

Firebase Security Rules untuk Cloud Storage juga dapat digunakan untuk validasi data, termasuk memvalidasi nama dan jalur file serta properti metadata file seperti contentType dan size.

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

Fungsi kustom

Seiring Firebase Security Rules Anda bertambah kompleks, Anda mungkin ingin mengemas kumpulan kondisi ke dalam fungsi yang dapat digunakan kembali di semua kumpulan aturan Anda. Aturan keamanan mendukung fungsi kustom. Sintaksis untuk fungsi kustom mirip dengan JavaScript, tetapi fungsi Firebase Security Rules ditulis dalam bahasa khusus domain yang memiliki beberapa batasan penting:

  • Fungsi hanya dapat berisi satu pernyataan return. Fungsi tidak boleh berisi logika lain apa pun. Misalnya, fungsi tidak dapat menjalankan loop atau memanggil layanan eksternal.
  • Fungsi dapat secara otomatis mengakses fungsi dan variabel dari cakupan tempat ditetapkannya. Misalnya, fungsi yang ditetapkan ke dalam cakupan service firebase.storage memiliki akses ke variabel resource, dan khusus Cloud Firestore, memiliki fungsi bawaan seperti get() dan exists().
  • Sebuah fungsi dapat memanggil fungsi lain namun tidak secara berulang. Total kedalaman stack panggilan dibatasi sampai 10.
  • Pada versi rules2, fungsi dapat menentukan variabel menggunakan kata kunci let. Fungsi dapat memiliki berapa pun binding let, tetapi harus diakhiri dengan pernyataan return.

Fungsi ditetapkan dengan kata kunci function dan menerima nol argumen atau lebih. Misalnya, Anda mungkin ingin menggabungkan dua jenis kondisi yang digunakan dalam contoh di atas menjadi sebuah fungsi:

service firebase.storage {
  match /b/{bucket}/o {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }
    match /images/{imageId} {
      allow read, write: if signedInOrPublic();
    }
    match /mp3s/{mp3Ids} {
      allow read: if signedInOrPublic();
    }
  }
}

Dengan menggunakan fungsi di Firebase Security Rules, aturan akan lebih mudah dipertahankan seiring semakin kompleksnya aturan Anda.

Langkah berikutnya

Setelah membaca pembahasan kondisi di atas, Anda telah memiliki pemahaman yang lebih mendalam tentang Aturan dan siap untuk:

Mempelajari cara menangani kasus penggunaan inti, dan mempelajari alur kerja untuk mengembangkan, menguji, dan men-deploy Aturan: