Ikuti semua informasi yang diumumkan di Firebase Summit, dan pelajari bagaimana Firebase dapat membantu Anda mempercepat pengembangan aplikasi dan menjalankan aplikasi dengan percaya diri. Pelajari Lebih Lanjut

Gunakan ketentuan dalam Aturan Keamanan Database Realtime

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

Panduan ini dibuat berdasarkan mempelajari panduan bahasa Aturan Keamanan Firebase inti untuk menunjukkan cara menambahkan ketentuan ke Aturan Keamanan Firebase Realtime Database Anda.

Blok bangunan utama Aturan Keamanan Realtime Database adalah kondisinya . Kondisi adalah ekspresi Boolean yang menentukan apakah operasi tertentu harus diizinkan atau ditolak. Untuk aturan dasar, menggunakan literal true dan false sebagai kondisi bekerja dengan sangat baik. Tetapi bahasa Aturan Keamanan Database Realtime memberi Anda cara untuk menulis kondisi yang lebih kompleks yang dapat:

  • Periksa otentikasi pengguna
  • Mengevaluasi data yang ada terhadap data yang baru dikirimkan
  • Akses dan bandingkan berbagai bagian database Anda
  • Validasi data yang masuk
  • Gunakan struktur kueri masuk untuk logika keamanan

Menggunakan Variabel $ untuk Menangkap Segmen Jalur

Anda dapat menangkap bagian dari jalur untuk membaca atau menulis dengan mendeklarasikan variabel tangkap dengan awalan $ . Ini berfungsi sebagai wild card, dan menyimpan nilai kunci itu untuk digunakan di dalam ketentuan aturan:

{
  "rules": {
    "rooms": {
      // this rule applies to any child of /rooms/, the key for each room id
      // is stored inside $room_id variable for reference
      "$room_id": {
        "topic": {
          // the room's topic can be changed if the room id has "public" in it
          ".write": "$room_id.contains('public')"
        }
      }
    }
  }
}

Variabel $ dinamis juga dapat digunakan secara paralel dengan nama jalur konstan. Dalam contoh ini, kami menggunakan variabel $other untuk mendeklarasikan aturan .validate yang memastikan bahwa widget tidak memiliki turunan selain title dan color . Setiap penulisan yang akan menghasilkan anak-anak tambahan yang dibuat akan gagal.

{
  "rules": {
    "widget": {
      // a widget can have a title or color attribute
      "title": { ".validate": true },
      "color": { ".validate": true },

      // but no other child paths are allowed
      // in this case, $other means any key excluding "title" and "color"
      "$other": { ".validate": false }
    }
  }
}

Autentikasi

Salah satu pola aturan keamanan yang paling umum adalah mengontrol akses berdasarkan status otentikasi pengguna. Misalnya, aplikasi Anda mungkin ingin mengizinkan hanya pengguna yang masuk untuk menulis data.

Jika aplikasi Anda menggunakan Firebase Authentication, variabel request.auth berisi informasi autentikasi untuk klien yang meminta data. Untuk informasi lebih lanjut tentang request.auth , lihat dokumentasi referensi .

Firebase Authentication terintegrasi dengan Firebase Realtime Database untuk memungkinkan Anda mengontrol akses data per pengguna menggunakan kondisi. Setelah pengguna mengautentikasi, variabel auth dalam aturan Aturan Keamanan Realtime Database Anda akan diisi dengan informasi pengguna. Informasi ini mencakup pengenal unik mereka ( uid ) serta data akun tertaut, seperti id Facebook atau alamat email, dan info lainnya. Jika Anda menerapkan penyedia autentikasi khusus, Anda dapat menambahkan bidang Anda sendiri ke payload autentikasi pengguna Anda.

Bagian ini menjelaskan cara menggabungkan bahasa Aturan Keamanan Firebase Realtime Database dengan informasi autentikasi tentang pengguna Anda. Dengan menggabungkan dua konsep ini, Anda dapat mengontrol akses ke data berdasarkan identitas pengguna.

Variabel auth

Variabel auth yang telah ditentukan dalam aturan adalah nol sebelum autentikasi dilakukan.

Setelah pengguna diautentikasi dengan Firebase Authentication , ia akan berisi atribut berikut:

pemberi Metode otentikasi yang digunakan ("password", "anonymous", "facebook", "github", "google", atau "twitter").
uid ID pengguna unik, dijamin unik di semua penyedia.
token Konten token ID Firebase Auth. Lihat dokumentasi referensi untuk auth.token untuk lebih jelasnya.

Berikut adalah contoh aturan yang menggunakan variabel auth untuk memastikan bahwa setiap pengguna hanya dapat menulis ke jalur khusus pengguna:

{
  "rules": {
    "users": {
      "$user_id": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($user_id)
        ".write": "$user_id === auth.uid"
      }
    }
  }
}

Penataan Basis Data Anda untuk Mendukung Kondisi Otentikasi

Biasanya membantu untuk menyusun database Anda dengan cara yang membuat penulisan Aturan lebih mudah. Salah satu pola umum untuk menyimpan data pengguna di Realtime Database adalah menyimpan semua pengguna Anda dalam satu node users yang turunannya adalah nilai uid untuk setiap pengguna. Jika Anda ingin membatasi akses ke data ini sehingga hanya pengguna yang masuk yang dapat melihat data mereka sendiri, aturan Anda akan terlihat seperti ini.

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth !== null && auth.uid === $uid"
      }
    }
  }
}

Bekerja dengan Klaim Kustom Otentikasi

Untuk aplikasi yang memerlukan kontrol akses khusus untuk pengguna yang berbeda, Firebase Authentication memungkinkan developer menetapkan klaim pada pengguna Firebase . Klaim ini dapat diakses di variabel auth.token di aturan Anda. Berikut adalah contoh aturan yang menggunakan klaim khusus hasEmergencyTowel :

{
  "rules": {
    "frood": {
      // A towel is about the most massively useful thing an interstellar
      // hitchhiker can have
      ".read": "auth.token.hasEmergencyTowel === true"
    }
  }
}

Pengembang yang membuat token autentikasi kustom mereka sendiri dapat secara opsional menambahkan klaim ke token ini. Klaim ini tersedia di variabel auth.token di aturan Anda.

Data yang Ada vs. Data Baru

Variabel data yang telah ditentukan digunakan untuk merujuk ke data sebelum operasi tulis berlangsung. Sebaliknya, variabel newData berisi data baru yang akan ada jika operasi tulis berhasil. newData mewakili hasil gabungan dari data baru yang sedang ditulis dan data yang sudah ada.

Sebagai ilustrasi, aturan ini akan memungkinkan kita untuk membuat catatan baru atau menghapus yang sudah ada, tetapi tidak membuat perubahan pada data non-null yang ada:

// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"

Referensi Data di Jalur lain

Setiap data dapat digunakan sebagai kriteria untuk aturan. Menggunakan variabel yang telah ditentukan sebelumnya root , data , dan newData , kita dapat mengakses jalur apa pun seperti yang akan ada sebelum atau setelah acara tulis.

Pertimbangkan contoh ini, yang memungkinkan operasi tulis selama nilai /allow_writes/ node adalah true , node induk tidak memiliki set flag readOnly , dan ada anak bernama foo dalam data yang baru ditulis:

".write": "root.child('allow_writes').val() === true &&
          !data.parent().child('readOnly').exists() &&
          newData.child('foo').exists()"

Memvalidasi Data

Menegakkan struktur data dan memvalidasi format dan konten data harus dilakukan menggunakan aturan .validate , yang dijalankan hanya setelah aturan .write berhasil memberikan akses. Di bawah ini adalah contoh definisi aturan .validate yang hanya mengizinkan tanggal dalam format YYYY-MM-DD antara tahun 1900-2099, yang diperiksa menggunakan ekspresi reguler.

".validate": "newData.isString() &&
              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"

Aturan .validate adalah satu-satunya jenis aturan keamanan yang tidak berjenjang. Jika ada aturan validasi yang gagal pada rekaman anak mana pun, seluruh operasi penulisan akan ditolak. Selain itu, definisi validasi diabaikan saat data dihapus (yaitu, saat nilai baru yang ditulis adalah null ).

Ini mungkin tampak seperti poin sepele, tetapi sebenarnya merupakan fitur penting untuk menulis Aturan Keamanan Firebase Realtime Database yang kuat. Pertimbangkan aturan berikut:

{
  "rules": {
    // write is allowed for all paths
    ".write": true,
    "widget": {
      // a valid widget must have attributes "color" and "size"
      // allows deleting widgets (since .validate is not applied to delete rules)
      ".validate": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99
        ".validate": "newData.isNumber() &&
                      newData.val() >= 0 &&
                      newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical
        // /valid_colors/ index
        ".validate": "root.child('valid_colors/' + newData.val()).exists()"
      }
    }
  }
}

Dengan mempertimbangkan varian ini, lihat hasil untuk operasi tulis berikut:

JavaScript
var ref = db.ref("/widget");

// PERMISSION_DENIED: does not have children color and size
ref.set('foo');

// PERMISSION DENIED: does not have child color
ref.set({size: 22});

// PERMISSION_DENIED: size is not a number
ref.set({ size: 'foo', color: 'red' });

// SUCCESS (assuming 'blue' appears in our colors list)
ref.set({ size: 21, color: 'blue'});

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child('size').set(99);
Objective-C
Catatan: Produk Firebase ini tidak tersedia di target Klip Aplikasi.
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"];

// PERMISSION_DENIED: does not have children color and size
[ref setValue: @"foo"];

// PERMISSION DENIED: does not have child color
[ref setValue: @{ @"size": @"foo" }];

// PERMISSION_DENIED: size is not a number
[ref setValue: @{ @"size": @"foo", @"color": @"red" }];

// SUCCESS (assuming 'blue' appears in our colors list)
[ref setValue: @{ @"size": @21, @"color": @"blue" }];

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
[[ref child:@"size"] setValue: @99];
Cepat
Catatan: Produk Firebase ini tidak tersedia di target Klip Aplikasi.
var ref = FIRDatabase.database().reference().child("widget")

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo")

// PERMISSION DENIED: does not have child color
ref.setValue(["size": "foo"])

// PERMISSION_DENIED: size is not a number
ref.setValue(["size": "foo", "color": "red"])

// SUCCESS (assuming 'blue' appears in our colors list)
ref.setValue(["size": 21, "color": "blue"])

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
Jawa
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("widget");

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo");

// PERMISSION DENIED: does not have child color
ref.child("size").setValue(22);

// PERMISSION_DENIED: size is not a number
Map<String,Object> map = new HashMap<String, Object>();
map.put("size","foo");
map.put("color","red");
ref.setValue(map);

// SUCCESS (assuming 'blue' appears in our colors list)
map = new HashMap<String, Object>();
map.put("size", 21);
map.put("color","blue");
ref.setValue(map);

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
ISTIRAHAT
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Sekarang mari kita lihat struktur yang sama, tetapi menggunakan aturan .write alih-alih .validate :

{
  "rules": {
    // this variant will NOT allow deleting records (since .write would be disallowed)
    "widget": {
      // a widget must have 'color' and 'size' in order to be written to this path
      ".write": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE
        ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical valid_colors/ index
        // BUT ONLY IF WE WRITE DIRECTLY TO COLOR
        ".write": "root.child('valid_colors/'+newData.val()).exists()"
      }
    }
  }
}

Dalam varian ini, salah satu operasi berikut akan berhasil:

JavaScript
var ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.set({size: 99999, color: 'red'});

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child('size').set(99);
Objective-C
Catatan: Produk Firebase ini tidak tersedia di target Klip Aplikasi.
Firebase *ref = [[Firebase alloc] initWithUrl:URL];

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
[ref setValue: @{ @"size": @9999, @"color": @"red" }];

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
[[ref childByAppendingPath:@"size"] setValue: @99];
Cepat
Catatan: Produk Firebase ini tidak tersedia di target Klip Aplikasi.
var ref = Firebase(url:URL)

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.setValue(["size": 9999, "color": "red"])

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.childByAppendingPath("size").setValue(99)
Jawa
Firebase ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
Map<String,Object> map = new HashMap<String, Object>();
map.put("size", 99999);
map.put("color", "red");
ref.setValue(map);

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child("size").setValue(99);
ISTIRAHAT
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Ini menggambarkan perbedaan antara aturan .write dan .validate . Seperti yang ditunjukkan, semua aturan ini harus ditulis menggunakan .validate , dengan kemungkinan pengecualian dari aturan newData.hasChildren() , yang akan bergantung pada apakah penghapusan harus diizinkan.

Aturan berbasis kueri

Meskipun Anda tidak dapat menggunakan aturan sebagai filter , Anda dapat membatasi akses ke subkumpulan data dengan menggunakan parameter kueri dalam aturan Anda. Gunakan query. ekspresi dalam aturan Anda untuk memberikan akses baca atau tulis berdasarkan parameter kueri.

Misalnya, aturan berbasis kueri berikut menggunakan aturan keamanan berbasis pengguna dan aturan berbasis kueri untuk membatasi akses ke data dalam kumpulan baskets hanya ke keranjang belanja yang dimiliki pengguna aktif:

"baskets": {
  ".read": "auth.uid !== null &&
            query.orderByChild === 'owner' &&
            query.equalTo === auth.uid" // restrict basket access to owner of basket
}

Kueri berikut, yang menyertakan parameter kueri dalam aturan, akan berhasil:

db.ref("baskets").orderByChild("owner")
                 .equalTo(auth.currentUser.uid)
                 .on("value", cb)                 // Would succeed

Namun, kueri yang tidak menyertakan parameter dalam aturan akan gagal dengan kesalahan PermissionDenied :

db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied

Anda juga dapat menggunakan aturan berbasis kueri untuk membatasi jumlah data yang diunduh klien melalui operasi baca.

Misalnya, aturan berikut membatasi akses baca hanya ke 1000 hasil kueri pertama, seperti yang diurutkan berdasarkan prioritas:

messages: {
  ".read": "query.orderByKey &&
            query.limitToFirst <= 1000"
}

// Example queries:

db.ref("messages").on("value", cb)                // Would fail with PermissionDenied

db.ref("messages").limitToFirst(1000)
                  .on("value", cb)                // Would succeed (default order by key)

query. ekspresi tersedia di Aturan Keamanan Database Realtime.

Ekspresi aturan berbasis kueri
Ekspresi Jenis Keterangan
query.orderByKey
query.orderByPriority
query.orderByValue
boolean Benar untuk kueri yang diurutkan berdasarkan kunci, prioritas, atau nilai. Salah sebaliknya.
query.orderByChild rangkaian
batal
Gunakan string untuk mewakili jalur relatif ke simpul anak. Misalnya, query.orderByChild === "address/zip" . Jika kueri tidak diurutkan oleh simpul anak, nilai ini adalah nol.
query.startAt
query.endAt
query.equalTo
rangkaian
nomor
boolean
batal
Mengambil batas kueri yang dieksekusi, atau mengembalikan null jika tidak ada kumpulan terikat.
query.limitToFirst
query.limitToLast
nomor
batal
Mengambil batas pada kueri yang dieksekusi, atau mengembalikan null jika tidak ada batas yang ditetapkan.

Langkah selanjutnya

Setelah diskusi tentang kondisi ini, Anda memiliki pemahaman Aturan yang lebih canggih dan siap untuk:

Pelajari cara menangani kasus penggunaan inti, dan pelajari alur kerja untuk mengembangkan, menguji, dan menerapkan Aturan:

Pelajari fitur Aturan yang khusus untuk Realtime Database: