Ngôn ngữ quy tắc bảo mật

Quy tắc bảo mật của Firebase tận dụng các ngôn ngữ tùy chỉnh, mạnh mẽ, linh hoạt, hỗ trợ nhiều mức độ phức tạp và chi tiết. Bạn có thể đặt Quy tắc của mình cụ thể hoặc chung chung sao cho phù hợp với ứng dụng của mình. Các quy tắc Cơ sở dữ liệu thời gian thực sử dụng cú pháp trông giống như JavaScript trong cấu trúc JSON. Các quy tắc của Cloud Firestore và Cloud Storage sử dụng ngôn ngữ dựa trên Ngôn ngữ biểu thức chung (CEL) , được xây dựng trên CEL với các câu lệnh matchallow hỗ trợ quyền truy cập được cấp có điều kiện.

Tuy nhiên, vì đây là những ngôn ngữ tùy chỉnh nên cần có một lộ trình học tập. Hãy sử dụng hướng dẫn này để hiểu rõ hơn về ngôn ngữ Quy tắc khi bạn tìm hiểu sâu hơn về các quy tắc phức tạp hơn.

Chọn một sản phẩm để tìm hiểu thêm về các quy tắc của nó.

Cấu trúc cơ bản

Cửa hàng đám mây

Quy tắc bảo mật Firebase trong Cloud Firestore và Cloud Storage sử dụng cấu trúc và cú pháp sau:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Các khái niệm chính sau đây rất quan trọng để hiểu khi bạn xây dựng các quy tắc:

  • Yêu cầu: Phương thức hoặc các phương thức được gọi trong câu lệnh allow . Đây là những phương pháp bạn cho phép chạy. Các phương thức tiêu chuẩn là: get , list , create , updatedelete . Các phương thức readwrite thuận tiện cho phép truy cập đọc và ghi rộng rãi trên cơ sở dữ liệu hoặc đường dẫn lưu trữ được chỉ định.
  • Đường dẫn: Cơ sở dữ liệu hoặc vị trí lưu trữ, được biểu thị dưới dạng đường dẫn URI.
  • Quy tắc: Câu lệnh allow , bao gồm một điều kiện cho phép một yêu cầu nếu nó được đánh giá là đúng.

Mỗi khái niệm này sẽ được mô tả chi tiết hơn dưới đây.

Lưu trữ đám mây

Quy tắc bảo mật Firebase trong Cloud Firestore và Cloud Storage sử dụng cấu trúc và cú pháp sau:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Các khái niệm chính sau đây rất quan trọng để hiểu khi bạn xây dựng các quy tắc:

  • Yêu cầu: Phương thức hoặc các phương thức được gọi trong câu lệnh allow . Đây là những phương pháp bạn cho phép chạy. Các phương thức tiêu chuẩn là: get , list , create , updatedelete . Các phương thức readwrite thuận tiện cho phép truy cập đọc và ghi rộng rãi trên cơ sở dữ liệu hoặc đường dẫn lưu trữ được chỉ định.
  • Đường dẫn: Cơ sở dữ liệu hoặc vị trí lưu trữ, được biểu thị dưới dạng đường dẫn URI.
  • Quy tắc: Câu lệnh allow , bao gồm một điều kiện cho phép một yêu cầu nếu nó được đánh giá là đúng.

Mỗi khái niệm này sẽ được mô tả chi tiết hơn dưới đây.

Cơ sở dữ liệu thời gian thực

Trong Cơ sở dữ liệu thời gian thực, Quy tắc bảo mật Firebase bao gồm các biểu thức giống JavaScript có trong tài liệu JSON.

Họ sử dụng cú pháp sau:

{
  "rules": {
    "<<path>>": {
    // Allow the request if the condition for each method is true.
      ".read": <<condition>>,
      ".write": <<condition>>,
      ".validate": <<condition>>
    }
  }
}

Có ba yếu tố cơ bản trong quy tắc:

  • Đường dẫn: Vị trí cơ sở dữ liệu. Điều này phản ánh cấu trúc JSON của cơ sở dữ liệu của bạn.
  • Yêu cầu: Đây là các phương thức mà quy tắc sử dụng để cấp quyền truy cập. Quy tắc readwrite quyền truy cập đọc và ghi rộng rãi, trong khi quy tắc validate đóng vai trò xác minh phụ để cấp quyền truy cập dựa trên dữ liệu đến hoặc hiện có.
  • Điều kiện: Điều kiện cho phép yêu cầu nếu nó đánh giá là đúng.

Cấu trúc quy tắc

Cửa hàng đám mây

Các thành phần cơ bản của một quy tắc trong Cloud Firestore và Cloud Storage như sau:

  • Khai báo service : Khai báo sản phẩm Firebase mà các quy tắc áp dụng.
  • Khối match khớp: Xác định đường dẫn trong cơ sở dữ liệu hoặc nhóm lưu trữ mà các quy tắc áp dụng.
  • Câu lệnh allow : Cung cấp các điều kiện để cấp quyền truy cập, được phân biệt bằng các phương thức. Các phương thức được hỗ trợ bao gồm: get , list , create , update , delete và các phương thức tiện lợi readwrite .
  • Khai báo function tùy chọn: Cung cấp khả năng kết hợp và bao bọc các điều kiện để sử dụng trên nhiều quy tắc.

service này chứa một hoặc nhiều khối match với các câu lệnh allow cung cấp các điều kiện cấp quyền truy cập vào các yêu cầu. Các biến requestresource có sẵn để sử dụng trong các điều kiện quy tắc. Ngôn ngữ Quy tắc bảo mật Firebase cũng hỗ trợ khai báo function .

Phiên bản cú pháp

Câu lệnh syntax cho biết phiên bản ngôn ngữ Quy tắc Firebase được sử dụng để ghi nguồn. Phiên bản mới nhất của ngôn ngữ này là v2 .

rules_version = '2';
service cloud.firestore {
...
}

Nếu không có câu lệnh rules_version nào được cung cấp thì các quy tắc của bạn sẽ được đánh giá bằng công cụ v1 .

Dịch vụ

Tuyên bố service xác định sản phẩm hoặc dịch vụ Firebase nào mà quy tắc của bạn áp dụng. Bạn chỉ có thể bao gồm một khai báo service cho mỗi tệp nguồn.

Cửa hàng đám mây

service cloud.firestore {
 // Your 'match' blocks with their corresponding 'allow' statements and
 // optional 'function' declarations are contained here
}

Lưu trữ đám mây

service firebase.storage {
  // Your 'match' blocks with their corresponding 'allow' statements and
  // optional 'function' declarations are contained here
}

Nếu bạn đang xác định quy tắc cho cả Cloud Firestore và Cloud Storage bằng Firebase CLI, bạn sẽ phải duy trì chúng trong các tệp riêng biệt.

Cuộc thi đấu

Khối match khớp khai báo một mẫu path khớp với đường dẫn cho thao tác được yêu cầu ( request.path đến). Phần thân của match phải có một hoặc nhiều khối match lồng nhau, câu lệnh allow hoặc khai báo function . Đường dẫn trong khối match lồng nhau có liên quan đến đường dẫn trong khối match gốc.

Mẫu path là một tên giống thư mục có thể bao gồm các biến hoặc ký tự đại diện. Mẫu path cho phép khớp các phân đoạn một đường dẫn và nhiều đường dẫn. Bất kỳ biến nào bị ràng buộc trong một path đều hiển thị trong phạm vi match hoặc bất kỳ phạm vi lồng nhau nào nơi path được khai báo.

Sự trùng khớp với mẫu path có thể là một phần hoặc toàn bộ:

  • Khớp một phần: Mẫu path là khớp tiền tố của request.path .
  • Hoàn thành khớp: Mẫu path khớp với toàn bộ request.path .

Khi một kết quả khớp hoàn chỉnh được thực hiện, các quy tắc trong khối sẽ được đánh giá. Khi thực hiện khớp một phần , các quy tắc match lồng nhau sẽ được kiểm tra để xem liệu có path lồng nhau nào sẽ hoàn thành khớp hay không.

Các quy tắc trong mỗi match hoàn chỉnh được đánh giá để xác định xem có cho phép yêu cầu hay không. Nếu bất kỳ quy tắc phù hợp nào cấp quyền truy cập thì yêu cầu sẽ được cho phép. Nếu không có quy tắc phù hợp nào cấp quyền truy cập thì yêu cầu sẽ bị từ chối.

// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
  // Partial match.
  match /example/{singleSegment} {   // `singleSegment` == 'hello'
    allow write;                     // Write rule not evaluated.
    // Complete match.
    match /nested/path {             // `singleSegment` visible in scope.
      allow read;                    // Read rule is evaluated.
    }
  }
  // Complete match.
  match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
    allow read;                      // Read rule is evaluated.
  }
}

Như ví dụ trên cho thấy, việc khai báo path hỗ trợ các biến sau:

  • Ký tự đại diện phân đoạn đơn: Biến ký tự đại diện được khai báo trong đường dẫn bằng cách gói biến trong dấu ngoặc nhọn: {variable} . Biến này có thể truy cập được trong câu lệnh match dưới dạng một string .
  • Ký tự đại diện đệ quy: Ký tự đại diện đệ quy hoặc nhiều phân đoạn khớp với nhiều phân đoạn đường dẫn tại hoặc bên dưới một đường dẫn. Ký tự đại diện này khớp với tất cả các đường dẫn bên dưới vị trí bạn đặt. Bạn có thể khai báo nó bằng cách thêm chuỗi =** vào cuối biến phân đoạn của mình: {variable=**} . Biến này có thể truy cập được trong câu lệnh match dưới dạng đối tượng path .

Cho phép

match chứa một hoặc nhiều câu lệnh allow . Đây là những quy tắc thực tế của bạn. Bạn có thể áp dụng quy tắc allow cho một hoặc nhiều phương thức. Các điều kiện trên câu lệnh allow phải đánh giá là đúng để Cloud Firestore hoặc Cloud Storage chấp nhận mọi yêu cầu gửi đến. Bạn cũng có thể viết các câu lệnh allow không có điều kiện, ví dụ: allow read . Tuy nhiên, nếu câu lệnh allow không bao gồm một điều kiện, thì nó luôn cho phép yêu cầu đối với phương thức đó.

Nếu bất kỳ quy tắc allow nào của phương thức được thỏa mãn thì yêu cầu sẽ được cho phép. Ngoài ra, nếu quy tắc rộng hơn cấp quyền truy cập thì Quy tắc sẽ cấp quyền truy cập và bỏ qua mọi quy tắc chi tiết hơn có thể hạn chế quyền truy cập.

Hãy xem xét ví dụ sau, trong đó bất kỳ người dùng nào cũng có thể đọc hoặc xóa bất kỳ tệp nào của riêng họ. Quy tắc chi tiết hơn chỉ cho phép ghi nếu người dùng yêu cầu ghi sở hữu tệp và tệp đó là PNG. Người dùng có thể xóa bất kỳ tệp nào tại đường dẫn phụ — ngay cả khi chúng không phải là PNG — vì quy tắc trước đó cho phép điều đó.

service firebase.storage {
  // Allow the requestor to read or delete any resource on a path under the
  // user directory.
  match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
  }

  // Allow the requestor to create or update their own images.
  // When 'request.method' == 'delete' this rule and the one matching
  // any path under the user directory would both match and the `delete`
  // would be permitted.

  match /users/{userId}/images/{imageId} {
    // Whether to permit the request depends on the logical OR of all
    // matched rules. This means that even if this rule did not explicitly
    // allow the 'delete' the earlier rule would have.
    allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
  }
}

Phương pháp

Mỗi câu lệnh allow bao gồm một phương thức cấp quyền truy cập cho các yêu cầu đến của cùng một phương thức.

Phương pháp Loại yêu cầu
Phương pháp tiện lợi
read Bất kỳ loại yêu cầu đọc
write Bất kỳ loại yêu cầu viết
Phương pháp tiêu chuẩn
get Đọc yêu cầu cho các tài liệu hoặc tập tin riêng lẻ
list Đọc yêu cầu truy vấn và bộ sưu tập
create Viết tài liệu hoặc tập tin mới
update Ghi vào tài liệu cơ sở dữ liệu hiện có hoặc cập nhật siêu dữ liệu tệp
delete Xóa dữ liệu

Bạn không thể chồng chéo các phương thức đọc trong cùng một match hoặc các phương thức ghi xung đột trong cùng một khai báo path .

Ví dụ: các quy tắc sau sẽ thất bại:

service bad.example {
  match /rules/with/overlapping/methods {
    // This rule allows reads to all authenticated users
    allow read: if request.auth != null;

    match another/subpath {
      // This secondary, more specific read rule causes an error
      allow get: if request.auth != null && request.auth.uid == "me";
      // Overlapping write methods in the same path cause an error as well
      allow write: if request.auth != null;
      allow create: if request.auth != null && request.auth.uid == "me";
    }
  }
}

Chức năng

Khi các quy tắc bảo mật của bạn trở nên phức tạp hơn, bạn có thể muốn gói các tập hợp điều kiện vào các hàm mà bạn có thể sử dụng lại trên bộ quy tắc của mình. Quy tắc bảo mật hỗ trợ các chức năng tùy chỉnh. Cú pháp của các hàm tùy chỉnh hơi giống JavaScript, nhưng các hàm quy tắc bảo mật được viết bằng ngôn ngữ dành riêng cho miền có một số hạn chế quan trọng:

  • Các hàm chỉ có thể chứa một câu lệnh return duy nhất. Chúng không thể chứa bất kỳ logic bổ sung nào. Ví dụ: họ không thể thực hiện các vòng lặp hoặc gọi các dịch vụ bên ngoài.
  • Các hàm có thể tự động truy cập các hàm và biến từ phạm vi mà chúng được xác định. Ví dụ: một hàm được xác định trong phạm vi service cloud.firestore có quyền truy cập vào biến resource và các hàm tích hợp sẵn như get()exists() .
  • Các hàm có thể gọi các hàm khác nhưng không thể lặp lại. Tổng độ sâu ngăn xếp cuộc gọi được giới hạn ở 20.
  • Trong phiên bản quy tắc v2 , các hàm có thể xác định các biến bằng từ khóa let . Các hàm có thể có tối đa 10 liên kết let, nhưng phải kết thúc bằng câu lệnh return.

Một hàm được định nghĩa bằng từ khóa function và không có hoặc nhiều đối số. Ví dụ: bạn có thể muốn kết hợp hai loại điều kiện được sử dụng trong các ví dụ trên thành một hàm duy nhất:

service cloud.firestore {
  match /databases/{database}/documents {
    // 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 /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

Dưới đây là một ví dụ hiển thị các đối số của hàm và phép gán. Các câu lệnh gán phải cách nhau bằng dấu chấm phẩy.

function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
  return isAuthor || isAdmin;
}

Lưu ý cách gán isAdmin thực thi việc tra cứu bộ sưu tập quản trị viên. Để đánh giá lười biếng mà không yêu cầu tra cứu không cần thiết, hãy tận dụng tính chất ngắn mạch của && (AND) và || (HOẶC) so sánh để gọi hàm thứ hai chỉ khi isAuthor được hiển thị là đúng (đối với so sánh && ) hoặc sai (đối với so sánh || ).

function isAdmin(userId) {
  return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  // `||` is short-circuiting; isAdmin called only if isAuthor == false.
  return isAuthor || isAdmin(userId);
}

Việc sử dụng các hàm trong quy tắc bảo mật của bạn sẽ giúp chúng dễ bảo trì hơn khi độ phức tạp của các quy tắc của bạn tăng lên.

Lưu trữ đám mây

Các thành phần cơ bản của một quy tắc trong Cloud Firestore và Cloud Storage như sau:

  • Khai báo service : Khai báo sản phẩm Firebase mà các quy tắc áp dụng.
  • Khối match khớp: Xác định đường dẫn trong cơ sở dữ liệu hoặc nhóm lưu trữ mà các quy tắc áp dụng.
  • Câu lệnh allow : Cung cấp các điều kiện để cấp quyền truy cập, được phân biệt bằng các phương thức. Các phương thức được hỗ trợ bao gồm: get , list , create , update , delete và các phương thức tiện lợi readwrite .
  • Khai báo function tùy chọn: Cung cấp khả năng kết hợp và bao bọc các điều kiện để sử dụng trên nhiều quy tắc.

service này chứa một hoặc nhiều khối match với các câu lệnh allow cung cấp các điều kiện cấp quyền truy cập vào các yêu cầu. Các biến requestresource có sẵn để sử dụng trong các điều kiện quy tắc. Ngôn ngữ Quy tắc bảo mật Firebase cũng hỗ trợ khai báo function .

Phiên bản cú pháp

Câu lệnh syntax cho biết phiên bản ngôn ngữ Quy tắc Firebase được sử dụng để ghi nguồn. Phiên bản mới nhất của ngôn ngữ này là v2 .

rules_version = '2';
service cloud.firestore {
...
}

Nếu không có câu lệnh rules_version nào được cung cấp thì các quy tắc của bạn sẽ được đánh giá bằng công cụ v1 .

Dịch vụ

Tuyên bố service xác định sản phẩm hoặc dịch vụ Firebase nào mà quy tắc của bạn áp dụng. Bạn chỉ có thể bao gồm một khai báo service cho mỗi tệp nguồn.

Cửa hàng đám mây

service cloud.firestore {
 // Your 'match' blocks with their corresponding 'allow' statements and
 // optional 'function' declarations are contained here
}

Lưu trữ đám mây

service firebase.storage {
  // Your 'match' blocks with their corresponding 'allow' statements and
  // optional 'function' declarations are contained here
}

Nếu bạn đang xác định quy tắc cho cả Cloud Firestore và Cloud Storage bằng Firebase CLI, bạn sẽ phải duy trì chúng trong các tệp riêng biệt.

Cuộc thi đấu

Khối match khớp khai báo một mẫu path khớp với đường dẫn cho thao tác được yêu cầu ( request.path đến). Phần thân của match phải có một hoặc nhiều khối match lồng nhau, câu lệnh allow hoặc khai báo function . Đường dẫn trong khối match lồng nhau có liên quan đến đường dẫn trong khối match gốc.

Mẫu path là một tên giống thư mục có thể bao gồm các biến hoặc ký tự đại diện. Mẫu path cho phép khớp các phân đoạn một đường dẫn và nhiều đường dẫn. Bất kỳ biến nào bị ràng buộc trong một path đều hiển thị trong phạm vi match hoặc bất kỳ phạm vi lồng nhau nào nơi path được khai báo.

Sự trùng khớp với mẫu path có thể là một phần hoặc toàn bộ:

  • Khớp một phần: Mẫu path là khớp tiền tố của request.path .
  • Hoàn thành khớp: Mẫu path khớp với toàn bộ request.path .

Khi một kết quả khớp hoàn chỉnh được thực hiện, các quy tắc trong khối sẽ được đánh giá. Khi thực hiện khớp một phần , các quy tắc match lồng nhau sẽ được kiểm tra để xem liệu có path lồng nhau nào sẽ hoàn thành khớp hay không.

Các quy tắc trong mỗi match hoàn chỉnh được đánh giá để xác định xem có cho phép yêu cầu hay không. Nếu bất kỳ quy tắc phù hợp nào cấp quyền truy cập thì yêu cầu sẽ được cho phép. Nếu không có quy tắc phù hợp nào cấp quyền truy cập thì yêu cầu sẽ bị từ chối.

// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
  // Partial match.
  match /example/{singleSegment} {   // `singleSegment` == 'hello'
    allow write;                     // Write rule not evaluated.
    // Complete match.
    match /nested/path {             // `singleSegment` visible in scope.
      allow read;                    // Read rule is evaluated.
    }
  }
  // Complete match.
  match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
    allow read;                      // Read rule is evaluated.
  }
}

Như ví dụ trên cho thấy, việc khai báo path hỗ trợ các biến sau:

  • Ký tự đại diện phân đoạn đơn: Biến ký tự đại diện được khai báo trong đường dẫn bằng cách gói biến trong dấu ngoặc nhọn: {variable} . Biến này có thể truy cập được trong câu lệnh match dưới dạng một string .
  • Ký tự đại diện đệ quy: Ký tự đại diện đệ quy hoặc nhiều phân đoạn khớp với nhiều phân đoạn đường dẫn tại hoặc bên dưới một đường dẫn. Ký tự đại diện này khớp với tất cả các đường dẫn bên dưới vị trí bạn đặt. Bạn có thể khai báo nó bằng cách thêm chuỗi =** vào cuối biến phân đoạn của mình: {variable=**} . Biến này có thể truy cập được trong câu lệnh match dưới dạng đối tượng path .

Cho phép

match chứa một hoặc nhiều câu lệnh allow . Đây là những quy tắc thực tế của bạn. Bạn có thể áp dụng quy tắc allow cho một hoặc nhiều phương thức. Các điều kiện trên câu lệnh allow phải được đánh giá là đúng để Cloud Firestore hoặc Cloud Storage chấp nhận mọi yêu cầu gửi đến. Bạn cũng có thể viết các câu lệnh allow không có điều kiện, ví dụ: allow read . Tuy nhiên, nếu câu lệnh allow không bao gồm một điều kiện, thì nó luôn cho phép yêu cầu đối với phương thức đó.

Nếu bất kỳ quy tắc allow nào của phương thức được thỏa mãn thì yêu cầu sẽ được cho phép. Ngoài ra, nếu quy tắc rộng hơn cấp quyền truy cập thì Quy tắc sẽ cấp quyền truy cập và bỏ qua mọi quy tắc chi tiết hơn có thể hạn chế quyền truy cập.

Hãy xem xét ví dụ sau, trong đó bất kỳ người dùng nào cũng có thể đọc hoặc xóa bất kỳ tệp nào của riêng họ. Quy tắc chi tiết hơn chỉ cho phép ghi nếu người dùng yêu cầu ghi sở hữu tệp và tệp đó là PNG. Người dùng có thể xóa bất kỳ tệp nào tại đường dẫn phụ — ngay cả khi chúng không phải là PNG — vì quy tắc trước đó cho phép điều đó.

service firebase.storage {
  // Allow the requestor to read or delete any resource on a path under the
  // user directory.
  match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
  }

  // Allow the requestor to create or update their own images.
  // When 'request.method' == 'delete' this rule and the one matching
  // any path under the user directory would both match and the `delete`
  // would be permitted.

  match /users/{userId}/images/{imageId} {
    // Whether to permit the request depends on the logical OR of all
    // matched rules. This means that even if this rule did not explicitly
    // allow the 'delete' the earlier rule would have.
    allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
  }
}

Phương pháp

Mỗi câu lệnh allow bao gồm một phương thức cấp quyền truy cập cho các yêu cầu đến của cùng một phương thức.

Phương pháp Loại yêu cầu
Phương pháp tiện lợi
read Bất kỳ loại yêu cầu đọc
write Bất kỳ loại yêu cầu viết
Phương pháp tiêu chuẩn
get Đọc yêu cầu cho các tài liệu hoặc tập tin riêng lẻ
list Đọc yêu cầu truy vấn và bộ sưu tập
create Viết tài liệu hoặc tập tin mới
update Ghi vào tài liệu cơ sở dữ liệu hiện có hoặc cập nhật siêu dữ liệu tệp
delete Xóa dữ liệu

Bạn không thể chồng chéo các phương thức đọc trong cùng một match hoặc các phương thức ghi xung đột trong cùng một khai báo path .

Ví dụ: các quy tắc sau sẽ thất bại:

service bad.example {
  match /rules/with/overlapping/methods {
    // This rule allows reads to all authenticated users
    allow read: if request.auth != null;

    match another/subpath {
      // This secondary, more specific read rule causes an error
      allow get: if request.auth != null && request.auth.uid == "me";
      // Overlapping write methods in the same path cause an error as well
      allow write: if request.auth != null;
      allow create: if request.auth != null && request.auth.uid == "me";
    }
  }
}

Chức năng

Khi các quy tắc bảo mật của bạn trở nên phức tạp hơn, bạn có thể muốn gói các tập hợp điều kiện vào các hàm mà bạn có thể sử dụng lại trên bộ quy tắc của mình. Quy tắc bảo mật hỗ trợ các chức năng tùy chỉnh. Cú pháp của các hàm tùy chỉnh hơi giống JavaScript, nhưng các hàm quy tắc bảo mật được viết bằng ngôn ngữ dành riêng cho miền có một số hạn chế quan trọng:

  • Các hàm chỉ có thể chứa một câu lệnh return duy nhất. Chúng không thể chứa bất kỳ logic bổ sung nào. Ví dụ: họ không thể thực hiện các vòng lặp hoặc gọi các dịch vụ bên ngoài.
  • Các hàm có thể tự động truy cập các hàm và biến từ phạm vi mà chúng được xác định. Ví dụ: một hàm được xác định trong phạm vi service cloud.firestore có quyền truy cập vào biến resource và các hàm tích hợp sẵn như get()exists() .
  • Các hàm có thể gọi các hàm khác nhưng không thể lặp lại. Tổng độ sâu ngăn xếp cuộc gọi được giới hạn ở 20.
  • Trong phiên bản quy tắc v2 , các hàm có thể xác định các biến bằng từ khóa let . Các hàm có thể có tối đa 10 liên kết let, nhưng phải kết thúc bằng câu lệnh return.

Một hàm được định nghĩa bằng từ khóa function và không có hoặc nhiều đối số. Ví dụ: bạn có thể muốn kết hợp hai loại điều kiện được sử dụng trong các ví dụ trên thành một hàm duy nhất:

service cloud.firestore {
  match /databases/{database}/documents {
    // 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 /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

Dưới đây là một ví dụ hiển thị các đối số của hàm và phép gán let. Các câu lệnh gán phải cách nhau bằng dấu chấm phẩy.

function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
  return isAuthor || isAdmin;
}

Lưu ý cách gán isAdmin thực thi việc tra cứu bộ sưu tập quản trị viên. Để đánh giá lười biếng mà không yêu cầu tra cứu không cần thiết, hãy tận dụng tính chất ngắn mạch của && (AND) và || (HOẶC) so sánh để gọi hàm thứ hai chỉ khi isAuthor được hiển thị là đúng (đối với so sánh && ) hoặc sai (đối với so sánh || ).

function isAdmin(userId) {
  return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  // `||` is short-circuiting; isAdmin called only if isAuthor == false.
  return isAuthor || isAdmin(userId);
}

Việc sử dụng các hàm trong quy tắc bảo mật của bạn sẽ giúp chúng dễ bảo trì hơn khi độ phức tạp của các quy tắc của bạn tăng lên.

Cơ sở dữ liệu thời gian thực

Như đã nêu ở trên, Quy tắc cơ sở dữ liệu thời gian thực bao gồm ba thành phần cơ bản: vị trí cơ sở dữ liệu phản ánh cấu trúc JSON của cơ sở dữ liệu, loại yêu cầu và điều kiện cấp quyền truy cập.

Vị trí cơ sở dữ liệu

Cấu trúc quy tắc của bạn phải tuân theo cấu trúc dữ liệu bạn đã lưu trữ trong cơ sở dữ liệu của mình. Ví dụ: trong một ứng dụng trò chuyện có danh sách tin nhắn, bạn có thể có dữ liệu giống như sau:

  {
    "messages": {
      "message0": {
        "content": "Hello",
        "timestamp": 1405704370369
      },
      "message1": {
        "content": "Goodbye",
        "timestamp": 1405704395231
      },
      ...
    }
  }

Quy tắc của bạn nên phản ánh cấu trúc đó. Ví dụ:

  {
    "rules": {
      "messages": {
        "$message": {
          // only messages from the last ten minutes can be read
          ".read": "data.child('timestamp').val() > (now - 600000)",

          // new messages must have a string content and a number timestamp
          ".validate": "newData.hasChildren(['content', 'timestamp']) &&
                        newData.child('content').isString() &&
                        newData.child('timestamp').isNumber()"
        }
      }
    }
  }

Như ví dụ trên cho thấy, Quy tắc cơ sở dữ liệu thời gian thực hỗ trợ biến $location để khớp với các đoạn đường dẫn. Sử dụng tiền tố $ trước đoạn đường dẫn của bạn để khớp quy tắc của bạn với bất kỳ nút con nào dọc theo đường dẫn.

  {
    "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')"
          }
        }
      }
    }
  }

Bạn cũng có thể sử dụng $variable song song với tên đường dẫn không đổi.

  {
    "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 }
      }
    }
  }

Phương pháp

Trong Cơ sở dữ liệu thời gian thực, có ba loại quy tắc. Hai trong số các loại quy tắc này — readwrite — áp dụng cho phương thức của yêu cầu đến. Loại quy tắc validate thực thi cấu trúc dữ liệu và xác thực định dạng cũng như nội dung của dữ liệu. Quy tắc chạy quy tắc .validate sau khi xác minh rằng quy tắc .write cấp quyền truy cập.

Các loại quy tắc
.đọc Mô tả liệu và khi nào dữ liệu được phép đọc bởi người dùng.
.viết Mô tả liệu và khi nào dữ liệu được phép ghi.
.xác nhận Xác định giá trị được định dạng chính xác sẽ trông như thế nào, liệu giá trị đó có thuộc tính con và kiểu dữ liệu hay không.

Theo mặc định, nếu không có quy tắc nào cho phép điều đó thì quyền truy cập tại một đường dẫn sẽ bị từ chối.

Điều kiện xây dựng

Cửa hàng đám mây

Điều kiện là một biểu thức boolean xác định xem một thao tác cụ thể nên được cho phép hay từ chối. Các biến requestresource cung cấp ngữ cảnh cho các điều kiện đó.

Biến request

Biến request bao gồm các trường sau và thông tin tương ứng:

request.auth

Mã thông báo web JSON (JWT) chứa thông tin xác thực từ Xác thực Firebase. Mã thông báo auth chứa một tập hợp các xác nhận quyền sở hữu tiêu chuẩn và mọi xác nhận quyền sở hữu tùy chỉnh mà bạn tạo thông qua Xác thực Firebase. Tìm hiểu thêm về Quy tắc bảo mật và xác thực Firebase .

request.method

request.method có thể là bất kỳ phương thức tiêu chuẩn nào hoặc phương thức tùy chỉnh. Các phương thức readwrite tiện lợi cũng tồn tại để đơn giản hóa các quy tắc viết áp dụng cho tất cả các phương pháp tiêu chuẩn chỉ đọc hoặc tất cả chỉ ghi tương ứng.

request.params

request.params bao gồm mọi dữ liệu không liên quan cụ thể đến request.resource có thể hữu ích cho việc đánh giá. Trong thực tế, bản đồ này phải trống cho tất cả các phương pháp tiêu chuẩn và phải chứa dữ liệu phi tài nguyên cho các phương pháp tùy chỉnh. Các dịch vụ phải cẩn thận để không đổi tên hoặc sửa đổi loại của bất kỳ khóa và giá trị nào được trình bày dưới dạng thông số.

request.path

request.path là đường dẫn cho resource đích. Đường dẫn có liên quan đến dịch vụ. Các đoạn đường dẫn chứa các ký tự an toàn không phải url như / được mã hóa url.

Biến resource nguyên

resource là giá trị hiện tại trong dịch vụ được biểu thị dưới dạng bản đồ các cặp khóa-giá trị. resource tham chiếu trong một điều kiện sẽ dẫn đến tối đa một lần đọc giá trị từ dịch vụ. Việc tra cứu này sẽ được tính vào mọi hạn ngạch liên quan đến dịch vụ cho tài nguyên. Đối với yêu cầu get , resource sẽ chỉ được tính vào hạn mức từ chối.

Toán tử và độ ưu tiên của toán tử

Sử dụng bảng bên dưới làm tài liệu tham khảo cho các nhà khai thác và mức độ ưu tiên tương ứng của họ trong Quy tắc dành cho Cloud Firestore và Cloud Storage.

Cho các biểu thức tùy ý ab , trường f và chỉ mục i .

Nhà điều hành Sự miêu tả tính kết hợp
a[i] a() af Chỉ mục, cuộc gọi, truy cập trường trái sang phải
!a -a Sự phủ định đơn nhất phải sang trái
a/ba%ba*b Toán tử nhân trái sang phải
a+b ab Toán tử cộng trái sang phải
a>ba>=ba Toán tử quan hệ trái sang phải
a in b Sự tồn tại trong danh sách hoặc bản đồ trái sang phải
a is type So sánh loại, trong đó type có thể là bool, int, float, số, chuỗi, danh sách, bản đồ, dấu thời gian, thời lượng, đường dẫn hoặc latlng trái sang phải
a==ba!=b Toán tử so sánh trái sang phải
a && b Có điều kiện VÀ trái sang phải
a || b HOẶC có điều kiện trái sang phải
a ? true_value : false_value Biểu hiện bậc ba trái sang phải

Lưu trữ đám mây

Điều kiện là một biểu thức boolean xác định xem một thao tác cụ thể nên được cho phép hay từ chối. Các biến requestresource cung cấp ngữ cảnh cho các điều kiện đó.

Biến request

Biến request bao gồm các trường sau và thông tin tương ứng:

request.auth

Mã thông báo web JSON (JWT) chứa thông tin xác thực từ Xác thực Firebase. Mã thông báo auth chứa một tập hợp các xác nhận quyền sở hữu tiêu chuẩn và mọi xác nhận quyền sở hữu tùy chỉnh mà bạn tạo thông qua Xác thực Firebase. Tìm hiểu thêm về Quy tắc bảo mật và xác thực Firebase .

request.method

request.method có thể là bất kỳ phương thức tiêu chuẩn nào hoặc phương thức tùy chỉnh. Các phương thức readwrite tiện lợi cũng tồn tại để đơn giản hóa các quy tắc viết áp dụng cho tất cả các phương pháp tiêu chuẩn chỉ đọc hoặc tất cả chỉ ghi tương ứng.

request.params

request.params bao gồm mọi dữ liệu không liên quan cụ thể đến request.resource có thể hữu ích cho việc đánh giá. Trong thực tế, bản đồ này phải trống cho tất cả các phương pháp tiêu chuẩn và phải chứa dữ liệu phi tài nguyên cho các phương pháp tùy chỉnh. Các dịch vụ phải cẩn thận để không đổi tên hoặc sửa đổi loại của bất kỳ khóa và giá trị nào được trình bày dưới dạng thông số.

request.path

request.path là đường dẫn cho resource đích. Đường dẫn có liên quan đến dịch vụ. Các đoạn đường dẫn chứa các ký tự an toàn không phải url như / được mã hóa url.

Biến resource nguyên

resource là giá trị hiện tại trong dịch vụ được biểu thị dưới dạng bản đồ các cặp khóa-giá trị. resource tham chiếu trong một điều kiện sẽ dẫn đến tối đa một lần đọc giá trị từ dịch vụ. Việc tra cứu này sẽ được tính vào mọi hạn ngạch liên quan đến dịch vụ cho tài nguyên. Đối với yêu cầu get , resource sẽ chỉ được tính vào hạn ngạch khi bị từ chối.

Toán tử và độ ưu tiên của toán tử

Sử dụng bảng bên dưới làm tài liệu tham khảo cho các nhà khai thác và mức độ ưu tiên tương ứng của họ trong Quy tắc dành cho Cloud Firestore và Cloud Storage.

Cho các biểu thức tùy ý ab , trường f và chỉ mục i .

Nhà điều hành Sự miêu tả tính kết hợp
a[i] a() af Chỉ mục, cuộc gọi, truy cập trường trái sang phải
!a -a Sự phủ định đơn nhất phải sang trái
a/ba%ba*b Toán tử nhân trái sang phải
a+b ab Toán tử cộng trái sang phải
a>ba>=ba Toán tử quan hệ trái sang phải
a in b Sự tồn tại trong danh sách hoặc bản đồ trái sang phải
a is type So sánh loại, trong đó type có thể là bool, int, float, số, chuỗi, danh sách, bản đồ, dấu thời gian, thời lượng, đường dẫn hoặc latlng trái sang phải
a==ba!=b Toán tử so sánh trái sang phải
a && b Có điều kiện VÀ trái sang phải
a || b HOẶC có điều kiện trái sang phải
a ? true_value : false_value Biểu hiện bậc ba trái sang phải

Cơ sở dữ liệu thời gian thực

Điều kiện là một biểu thức boolean xác định xem một thao tác cụ thể nên được cho phép hay từ chối. Bạn có thể xác định các điều kiện đó trong Quy tắc cơ sở dữ liệu thời gian thực theo những cách sau.

Các biến được xác định trước

Có một số biến hữu ích, được xác định trước có thể được truy cập bên trong định nghĩa quy tắc. Dưới đây là một bản tóm tắt ngắn gọn về mỗi:

Biến được xác định trước
Hiện nay Thời gian hiện tại tính bằng mili giây kể từ kỷ nguyên Linux. Điều này đặc biệt hiệu quả khi xác thực dấu thời gian được tạo bằng firebase.database.ServerValue.TIMESTAMP của SDK.
nguồn gốc RuleDataSnapshot đại diện cho đường dẫn gốc trong cơ sở dữ liệu Firebase vì nó tồn tại trước khi thực hiện thao tác.
dữ liệu mới RuleDataSnapshot thể hiện dữ liệu như nó sẽ tồn tại sau thao tác đã thử. Nó bao gồm dữ liệu mới được ghi và dữ liệu hiện có.
dữ liệu RuleDataSnapshot thể hiện dữ liệu như đã tồn tại trước khi thực hiện thao tác.
biến $ Đường dẫn ký tự đại diện được sử dụng để biểu thị id và khóa con động.
xác thực Đại diện cho tải trọng mã thông báo của người dùng đã được xác thực.

Bạn có thể sử dụng các biến này ở bất kỳ đâu trong quy tắc của mình. Ví dụ: các quy tắc bảo mật bên dưới đảm bảo rằng dữ liệu được ghi vào nút /foo/ phải là một chuỗi ít hơn 100 ký tự:

{
  "rules": {
    "foo": {
      // /foo is readable by the world
      ".read": true,

      // /foo is writable by the world
      ".write": true,

      // data written to /foo must be a string less than 100 characters
      ".validate": "newData.isString() && newData.val().length < 100"
    }
  }
}

Quy tắc dựa trên dữ liệu

Mọi dữ liệu trong cơ sở dữ liệu của bạn đều có thể được sử dụng trong các quy tắc của bạn. Bằng cách sử dụng các biến được xác định trước root , datanewData , bạn có thể truy cập bất kỳ đường dẫn nào tồn tại trước hoặc sau sự kiện ghi.

Hãy xem xét ví dụ này, cho phép các thao tác ghi miễn là giá trị của nút /allow_writes/true , nút cha không có bộ cờ readOnly và có một phần tử con có tên foo trong dữ liệu mới được ghi:

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

Quy tắc dựa trên truy vấn

Mặc dù bạn không thể sử dụng quy tắc làm bộ lọc nhưng bạn có thể giới hạn quyền truy cập vào các tập hợp con dữ liệu bằng cách sử dụng tham số truy vấn trong quy tắc của mình. Sử dụng query. biểu thức trong quy tắc của bạn để cấp quyền truy cập đọc hoặc ghi dựa trên các tham số truy vấn.

Ví dụ: quy tắc dựa trên truy vấn sau đây sử dụng quy tắc bảo mật dựa trên người dùng và quy tắc dựa trên truy vấn để hạn chế quyền truy cập vào dữ liệu trong bộ sưu tập baskets chỉ ở các giỏ hàng mà người dùng đang hoạt động sở hữu:

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

Truy vấn sau đây bao gồm các tham số truy vấn trong quy tắc sẽ thành công:

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

Tuy nhiên, các truy vấn không bao gồm các tham số trong quy tắc sẽ không thành công kèm theo lỗi PermissionDenied :

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

Bạn cũng có thể sử dụng các quy tắc dựa trên truy vấn để giới hạn lượng dữ liệu khách hàng tải xuống thông qua thao tác đọc.

Ví dụ: quy tắc sau chỉ giới hạn quyền truy cập đọc vào 1000 kết quả đầu tiên của truy vấn, theo thứ tự ưu tiên:

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)

Truy query. các biểu thức có sẵn trong Quy tắc bảo mật cơ sở dữ liệu thời gian thực.

Biểu thức quy tắc dựa trên truy vấn
Sự biểu lộ Kiểu Sự miêu tả
truy vấn.orderByKey
query.orderByPriority
truy vấn.orderByValue
boolean Đúng cho các truy vấn được sắp xếp theo khóa, mức độ ưu tiên hoặc giá trị. Sai nếu không.
truy vấn.orderByChild sợi dây
vô giá trị
Sử dụng một chuỗi để biểu thị đường dẫn tương đối đến nút con. Ví dụ: query.orderByChild === "address/zip" . Nếu truy vấn không được sắp xếp theo nút con thì giá trị này là null.
truy vấn.startAt
truy vấn.endAt
truy vấn.equalTo
sợi dây
con số
boolean
vô giá trị
Truy xuất các giới hạn của truy vấn đang thực thi hoặc trả về null nếu không có tập hợp giới hạn nào.
truy vấn.limitToFirst
truy vấn.limitToLast
con số
vô giá trị
Truy xuất giới hạn cho truy vấn đang thực thi hoặc trả về null nếu không có giới hạn nào được đặt.

Toán tử

Quy tắc cơ sở dữ liệu thời gian thực hỗ trợ một số toán tử mà bạn có thể sử dụng để kết hợp các biến trong câu lệnh điều kiện. Xem danh sách đầy đủ các toán tử trong tài liệu tham khảo .

Tạo điều kiện

Điều kiện thực tế của bạn sẽ thay đổi tùy theo quyền truy cập bạn muốn cấp. Các quy tắc có chủ đích mang lại mức độ linh hoạt rất lớn, do đó, các quy tắc trong ứng dụng của bạn cuối cùng có thể đơn giản hoặc phức tạp tùy theo nhu cầu của bạn.

Để biết một số hướng dẫn tạo Quy tắc đơn giản, sẵn sàng sản xuất, hãy xem Quy tắc bảo mật cơ bản .