Sử dụng các điều kiện trong Quy tắc bảo mật lưu trữ đám mây của Firebase

Hướng dẫn này được xây dựng dựa trên việc tìm hiểu cú pháp cốt lõi của hướng dẫn ngôn ngữ Quy tắc bảo mật Firebase để chỉ ra cách thêm điều kiện vào Quy tắc bảo mật Firebase cho Lưu trữ đám mây.

Khối xây dựng chính của Quy tắc bảo mật lưu trữ đám mây là điều kiện . Một điều kiện là một biểu thức boolean xác định xem một hoạt động cụ thể sẽ được cho phép hay từ chối. Đối với các quy tắc cơ bản, sử dụng nghĩa đen truefalse làm điều kiện hoạt động hoàn toàn tốt. Tuy nhiên, Quy tắc bảo mật Firebase cho ngôn ngữ Lưu trữ đám mây cung cấp cho bạn các cách để viết các điều kiện phức tạp hơn có thể:

  • Kiểm tra xác thực người dùng
  • Xác thực dữ liệu đến

xác thực

Quy tắc bảo mật Firebase cho Lưu trữ đám mây tích hợp với Xác thực Firebase để cung cấp xác thực mạnh mẽ dựa trên người dùng cho Lưu trữ đám mây. Điều này cho phép kiểm soát quyền truy cập chi tiết dựa trên các yêu cầu về mã thông báo Xác thực Firebase.

Khi người dùng được xác thực thực hiện yêu cầu đối với Cloud Storage, biến request.auth được điền bằng uid của người dùng ( request.auth.uid ) cũng như các yêu cầu của JWT xác thực Firebase ( request.auth.token ).

Ngoài ra, khi sử dụng xác thực tùy chỉnh, các yêu cầu bổ sung sẽ xuất hiện trong trường request.auth.token .

Khi người dùng chưa được xác thực thực hiện một yêu cầu, biến request.authnull .

Sử dụng dữ liệu này, có một số cách phổ biến để sử dụng xác thực để bảo mật tệp:

  • Công khai: bỏ qua request.auth
  • Đã xác thực riêng tư: kiểm tra xem request.auth có phải là null không
  • Người dùng riêng tư: kiểm tra xem request.auth.uid có bằng đường dẫn uid không
  • Nhóm riêng tư: kiểm tra xác nhận quyền sở hữu của mã thông báo tùy chỉnh để khớp với xác nhận quyền sở hữu đã chọn hoặc đọc siêu dữ liệu của tệp để xem trường siêu dữ liệu có tồn tại không

Công cộng

Bất kỳ quy tắc nào không xem xét ngữ cảnh request.auth đều có thể được coi là quy tắc public vì nó không xem xét ngữ cảnh xác thực của người dùng. Các quy tắc này có thể hữu ích để hiển thị dữ liệu công khai, chẳng hạn như nội dung trò chơi, tệp âm thanh hoặc nội dung tĩnh khác.

// 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");
}

Xác thực riêng tư

Trong một số trường hợp nhất định, bạn có thể muốn dữ liệu có thể xem được bởi tất cả người dùng được xác thực của ứng dụng của bạn, nhưng không phải bởi người dùng chưa được xác thực. Vì biến request.authnull đối với tất cả người dùng chưa được xác thực, nên tất cả những gì bạn phải làm là kiểm tra xem biến request.auth có tồn tại để yêu cầu xác thực hay không:

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

Người dùng riêng tư

Cho đến nay, trường hợp sử dụng phổ biến nhất cho request.auth sẽ là cung cấp cho người dùng cá nhân quyền chi tiết đối với tệp của họ: từ tải lên ảnh hồ sơ đến đọc tài liệu riêng tư.

Vì các tệp trong Cloud Storage có "đường dẫn" đầy đủ đến tệp, nên tất cả những gì cần thiết để tạo một tệp do người dùng kiểm soát là một phần thông tin nhận dạng người dùng, duy nhất trong tiền tố tên tệp (chẳng hạn như uid của người dùng) có thể được kiểm tra khi quy tắc được đánh giá:

// 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;
}

Nhóm riêng tư

Một trường hợp sử dụng phổ biến không kém khác là cho phép các quyền của nhóm đối với một đối tượng, chẳng hạn như cho phép một số thành viên trong nhóm cộng tác trên một tài liệu được chia sẻ. Có một số cách tiếp cận để làm điều này:

Khi dữ liệu này được lưu trữ trong mã thông báo hoặc siêu dữ liệu tệp, nó có thể được tham chiếu từ bên trong một quy tắc:

// 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;
}

Yêu cầu đánh giá

Tải lên, tải xuống, thay đổi siêu dữ liệu và xóa được đánh giá bằng cách sử dụng request được gửi tới Cloud Storage. Ngoài ID duy nhất của người dùng và tải trọng Xác thực Firebase trong đối tượng request.auth như được mô tả ở trên, biến request chứa đường dẫn tệp nơi yêu cầu đang được thực hiện, thời gian nhận được yêu cầu và giá trị resource mới nếu yêu cầu là một ghi.

Đối tượng request cũng chứa ID duy nhất của người dùng và tải trọng Xác thực Firebase trong đối tượng request.auth , sẽ được giải thích thêm trong phần Bảo mật dựa trên người dùng của tài liệu.

Dưới đây là danh sách đầy đủ các thuộc tính trong đối tượng request :

Tài sản Kiểu Sự miêu tả
auth bản đồ<chuỗi, chuỗi> Khi người dùng đăng nhập, hãy cung cấp uid , ID duy nhất của người dùng và token , bản đồ các xác nhận quyền sở hữu JWT của Firebase Authentication. Nếu không, nó sẽ là null .
params bản đồ<chuỗi, chuỗi> Bản đồ chứa các tham số truy vấn của yêu cầu.
path con đường path đại diện cho đường dẫn mà yêu cầu đang được thực hiện.
resource bản đồ<chuỗi, chuỗi> Giá trị tài nguyên mới, chỉ xuất hiện trên các yêu cầu write .
time dấu thời gian Dấu thời gian biểu thị thời gian máy chủ yêu cầu được đánh giá tại.

Đánh giá tài nguyên

Khi đánh giá các quy tắc, bạn cũng có thể muốn đánh giá siêu dữ liệu của tệp đang được tải lên, tải xuống, sửa đổi hoặc xóa. Điều này cho phép bạn tạo các quy tắc phức tạp và mạnh mẽ để thực hiện những việc như chỉ cho phép tải lên các tệp có loại nội dung nhất định hoặc chỉ xóa các tệp có kích thước lớn hơn một kích thước nhất định.

Quy tắc bảo mật Firebase cho Lưu trữ đám mây cung cấp siêu dữ liệu tệp trong đối tượng resource , chứa các cặp khóa/giá trị của siêu dữ liệu xuất hiện trong đối tượng Lưu trữ đám mây. Các thuộc tính này có thể được kiểm tra theo yêu cầu read hoặc write để đảm bảo tính toàn vẹn của dữ liệu.

Đối với các yêu cầu write (chẳng hạn như tải lên, cập nhật siêu dữ liệu và xóa), ngoài đối tượng tài nguyên chứa resource dữ liệu tệp cho tệp hiện đang tồn tại ở đường dẫn yêu cầu, bạn cũng có khả năng sử dụng đối tượng request.resource , trong đó chứa một tập hợp con của siêu dữ liệu tệp sẽ được ghi nếu việc ghi được cho phép. Bạn có thể sử dụng hai giá trị này để đảm bảo tính toàn vẹn của dữ liệu hoặc thực thi các ràng buộc của ứng dụng chẳng hạn như loại hoặc kích thước tệp.

Dưới đây là danh sách đầy đủ các thuộc tính trong đối tượng resource :

Tài sản Kiểu Sự miêu tả
name sợi dây Tên đầy đủ của đối tượng
bucket sợi dây Tên của vùng chứa đối tượng này.
generation int Quá trình tạo đối tượng Google Cloud Storage của đối tượng này.
metageneration int Siêu dữ liệu đối tượng Google Cloud Storage của đối tượng này.
size int Kích thước của đối tượng tính bằng byte.
timeCreated dấu thời gian Dấu thời gian biểu thị thời gian một đối tượng được tạo.
updated dấu thời gian Dấu thời gian biểu thị thời gian một đối tượng được cập nhật lần cuối.
md5Hash sợi dây Hàm băm MD5 của đối tượng.
crc32c sợi dây Hàm băm crc32c của đối tượng.
etag sợi dây etag được liên kết với đối tượng này.
contentDisposition sợi dây Bố cục nội dung được liên kết với đối tượng này.
contentEncoding sợi dây Mã hóa nội dung được liên kết với đối tượng này.
contentLanguage sợi dây Ngôn ngữ nội dung được liên kết với đối tượng này.
contentType sợi dây Loại nội dung được liên kết với đối tượng này.
metadata bản đồ<chuỗi, chuỗi> Các cặp khóa/giá trị của siêu dữ liệu tùy chỉnh bổ sung do nhà phát triển chỉ định.

request.resource chứa tất cả những thứ này ngoại trừ generation , metageneration , etag , timeCreatedupdated .

Tăng cường với Cloud Firestore

Bạn có thể truy cập tài liệu trong Cloud Firestore để đánh giá các tiêu chí ủy quyền khác.

Bằng cách sử dụng các hàm firestore.get()firestore.exists() , các quy tắc bảo mật của bạn có thể đánh giá các yêu cầu gửi đến đối với các tài liệu trong Cloud Firestore. Các hàm firestore.get()firestore.exists() đều mong đợi các đường dẫn tài liệu được chỉ định đầy đủ. Khi sử dụng các biến để xây dựng đường dẫn cho firestore.get()firestore.exists() , bạn cần thoát các biến một cách rõ ràng bằng cách sử dụng cú pháp $(variable) .

Trong ví dụ bên dưới, chúng ta thấy một quy tắc hạn chế quyền truy cập đọc tệp đối với những người dùng là thành viên của các câu lạc bộ cụ thể.

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
    }
  }
}
Trong ví dụ tiếp theo, chỉ bạn bè của người dùng mới có thể xem ảnh của họ.
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))
    }
  }
}

Khi bạn tạo và lưu Quy tắc bảo mật lưu trữ đám mây đầu tiên sử dụng các chức năng Cloud Firestore này, bạn sẽ được nhắc trong bảng điều khiển Firebase hoặc Firebase CLI để bật quyền kết nối hai sản phẩm.

Bạn có thể tắt tính năng này bằng cách xóa vai trò IAM, như được mô tả trong Quản lý và triển khai Quy tắc bảo mật Firebase .

Xác thực dữ liệu

Quy tắc bảo mật Firebase cho Lưu trữ đám mây cũng có thể được sử dụng để xác thực dữ liệu, bao gồm xác thực tên và đường dẫn tệp cũng như các thuộc tính siêu dữ liệu của tệp như contentTypesize .

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/.*');
    }
  }
}

chức năng tùy chỉnh

Khi Quy tắc bảo mật Firebase của bạn trở nên phức tạp hơn, bạn có thể muốn bao gồm các bộ điều kiện trong 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 của Quy tắc bảo mật Firebase đượ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ụ: chúng 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 firebase.storage có quyền truy cập vào biến resource và chỉ đối với Cloud Firestore, các hàm tích hợp sẵn như get()exists() .
  • Các chức năng có thể gọi các chức năng khác nhưng không được lặp lại. Tổng độ sâu ngăn xếp cuộc gọi được giới hạn ở 10.
  • Trong phiên bản rules2 , các hàm có thể xác định các biến bằng cách sử dụng từ khóa let . Các hàm có thể có bất kỳ số lượng ràng buộc let nào, nhưng phải kết thúc bằng câu lệnh return.

Một hàm được xác định bằng từ khóa function và không nhận 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 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();
    }
  }
}

Việc sử dụng các hàm trong Quy tắc bảo mật Firebase của bạn 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.

Bước tiếp theo

Sau cuộc thảo luận về các điều kiện này, bạn đã hiểu rõ hơn về Quy tắc và sẵn sàng:

Tìm hiểu cách xử lý các trường hợp sử dụng cốt lõi và tìm hiểu quy trình phát triển, thử nghiệm và triển khai Quy tắc: