Tìm hiểu cú pháp cốt lõi của ngôn ngữ Quy tắc bảo mật của Firebase dành cho Cloud Storage

Firebase Security Rules cho Cloud Storage cho phép bạn kiểm soát quyền truy cập vào các đối tượng được lưu trữ trong bộ chứa Cloud Storage. Cú pháp quy tắc linh hoạt cho phép bạn tạo quy tắc để kiểm soát mọi thao tác, từ tất cả các thao tác ghi vào bộ chứa Cloud Storage đến các thao tác trên một tệp cụ thể.

Hướng dẫn này mô tả cú pháp và cấu trúc cơ bản của Cloud Storage Security Rules để tạo bộ quy tắc hoàn chỉnh.

Khai báo dịch vụ và cơ sở dữ liệu

Firebase Security Rules cho Cloud Storage luôn bắt đầu bằng phần khai báo sau:

service firebase.storage {
    // ...
}

Phần khai báo service firebase.storage giới hạn phạm vi quy tắc đối với Cloud Storage, ngăn chặn xung đột giữa Cloud Storage Security Rules và quy tắc cho các sản phẩm khác như Cloud Firestore.

Quy tắc đọc/ghi cơ bản

Quy tắc cơ bản bao gồm câu lệnh match xác định Cloud Storage bộ chứa, câu lệnh match chỉ định tên tệp và biểu thức allow nêu chi tiết thời điểm được phép đọc dữ liệu đã chỉ định. allow biểu thức chỉ định phương thức truy cập (ví dụ: đọc, ghi) có liên quan và điều kiện mà theo đó quyền truy cập được cho phép hoặc bị từ chối.

Trong bộ quy tắc mặc định, câu lệnh match đầu tiên sử dụng biểu thức ký tự đại diện {bucket} wildcard để cho biết các quy tắc áp dụng cho tất cả bộ chứa trong dự án của bạn. Chúng ta sẽ thảo luận thêm về ý tưởng kết quả khớp với ký tự đại diện trong phần tiếp theo.

service firebase.storage {
  // The {bucket} wildcard indicates we match files in all Cloud Storage buckets
  match /b/{bucket}/o {
    // Match filename
    match /filename {
      allow read: if <condition>;
      allow write: if <condition>;
    }
  }
}

Tất cả câu lệnh match đều trỏ đến tệp. Câu lệnh match có thể trỏ đến một tệp cụ thể, như trong match /images/profilePhoto.png.

Ký tự đại diện khớp

Ngoài việc trỏ đến một tệp, Security Rules có thể sử dụng ký tự đại diện để trỏ đến bất kỳ tệp nào có tiền tố chuỗi đã cho trong tên, bao gồm cả dấu gạch chéo, như trong match /images/{imageId}.

Trong ví dụ trên, câu lệnh match sử dụng cú pháp ký tự đại diện {imageId}. Điều này có nghĩa là quy tắc áp dụng cho mọi tệp có /images/ ở đầu tên, chẳng hạn như /images/profilePhoto.png hoặc /images/croppedProfilePhoto.png. Khi các biểu thức trong câu lệnh match được đánh giá, biến sẽ phân giải thành tên tệp hình ảnh, chẳng hạn như hoặc.allowimageIdprofilePhoto.pngcroppedProfilePhoto.png

Bạn có thể tham chiếu biến ký tự đại diện từ trong match để cấp quyền cho tên tệp hoặc đường dẫn:

// Another way to restrict the name of a file
match /images/{imageId} {
  allow read: if imageId == "profilePhoto.png";
}

Dữ liệu phân cấp

Như chúng tôi đã nói trước đó, không có cấu trúc phân cấp nào bên trong bộ chứa Cloud Storage. Tuy nhiên, bằng cách sử dụng quy ước đặt tên tệp (thường là quy ước bao gồm dấu gạch chéo trong tên tệp), chúng ta có thể mô phỏng một cấu trúc trông giống như một chuỗi thư mục và thư mục con lồng nhau. Điều quan trọng là bạn phải hiểu cách Firebase Security Rules tương tác với các tên tệp này.

Hãy xem xét trường hợp một tập hợp tệp có tên đều bắt đầu bằng gốc /images/. Firebase Security Rules chỉ áp dụng cho tên tệp được so khớp, vì vậy, các quyền kiểm soát quyền truy cập được xác định trên gốc /images/ không áp dụng cho gốc /mp3s/. Thay vào đó, hãy viết các quy tắc rõ ràng khớp với các mẫu tên tệp khác nhau:

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      allow read, write: if <condition>;
    }

    // Explicitly define rules for the 'mp3s' pattern
    match /mp3s/{mp3Id} {
      allow read, write: if <condition>;
    }
  }
}

Khi lồng các câu lệnh match, đường dẫn của câu lệnh match bên trong luôn được thêm vào đường dẫn của câu lệnh match bên ngoài. Do đó, 2 bộ quy tắc sau đây tương đương:

service firebase.storage {
  match /b/{bucket}/o {
    match /images {
      // Exact match for "images/profilePhoto.png"
      match /profilePhoto.png {
        allow write: if <condition>;
      }
    }
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    // Exact match for "images/profilePhoto.png"
    match /images/profilePhoto.png {
      allow write: if <condition>;
      }
  }
}

Ký tự đại diện khớp đệ quy

Ngoài các ký tự đại diện khớp và trả về chuỗi ở cuối tên tệp, bạn có thể khai báo ký tự đại diện nhiều phân đoạn để so khớp phức tạp hơn bằng cách thêm =** vào tên ký tự đại diện, chẳng hạn như {path=**}:

// Partial match for files that start with "images"
match /images {

  // Exact match for "images/**"
  // e.g. images/users/user:12345/profilePhoto.png is matched
  // images/profilePhoto.png is also matched!
  match /{allImages=**} {
    // This rule matches one or more path segments (**)
    // allImages is a path that contains all segments matched
    allow read: if <other_condition>;
  }
}

Nếu nhiều quy tắc khớp với một tệp, thì kết quả là OR của kết quả đánh giá tất cả các quy tắc. Tức là nếu bất kỳ quy tắc nào mà tệp khớp đánh giá thành true, thì kết quả là true.

Trong các quy tắc ở trên, tệp "images/profilePhoto.png" có thể được đọc nếu một trong hai condition hoặc other_condition đánh giá thành true, trong khi tệp "images/users/user:12345/profilePhoto.png" chỉ tuân theo kết quả của other_condition.

Cloud Storage Security Rules không xếp tầng và các quy tắc chỉ được đánh giá khi đường dẫn yêu cầu khớp với đường dẫn có quy tắc được chỉ định.

Phiên bản 1

Firebase Security Rules sử dụng phiên bản 1 theo mặc định. Trong phiên bản 1, ký tự đại diện đệ quy khớp với một hoặc nhiều phần tử tên tệp, không phải 0 hoặc nhiều phần tử. Do đó, match /images/{filenamePrefixWildcard}/{imageFilename=**} khớp với tên tệp như /images/profilePics/profile.png, nhưng không khớp với /images/badge.png. Thay vào đó, hãy sử dụng /images/{imagePrefixorFilename=**}.

Ký tự đại diện đệ quy phải nằm ở cuối câu lệnh match.

Bạn nên sử dụng phiên bản 2 để có các tính năng mạnh mẽ hơn.

Phiên bản 2

Trong phiên bản 2 của Firebase Security Rules, ký tự đại diện đệ quy khớp với 0 hoặc nhiều mục đường dẫn. Do đó, /images/{filenamePrefixWildcard}/{imageFilename=**} khớp với tên tệp /images/profilePics/profile.png và /images/badge.png.

Bạn phải chọn sử dụng phiên bản 2 bằng cách thêm rules_version = '2'; ở đầu quy tắc bảo mật:

rules_version = '2';
service cloud.storage {
  match /b/{bucket}/o {
   ...
 }
}

Bạn có thể có tối đa một ký tự đại diện đệ quy cho mỗi câu lệnh match, nhưng trong phiên bản 2, bạn có thể đặt ký tự đại diện này ở bất kỳ đâu trong câu lệnh match. Ví dụ:

rules_version = '2';
service firebase.storage {
 match /b/{bucket}/o {
   // Matches any file in a songs "subdirectory" under the
   // top level of your Cloud Storage bucket.
   match /{prefixSegment=**}/songs/{mp3filenames} {
     allow read, write: if <condition>;
   }
  }
}

Thao tác chi tiết

Trong một số trường hợp, bạn nên chia readwrite thành các thao tác chi tiết hơn. Ví dụ: ứng dụng của bạn có thể muốn thực thi các điều kiện khác nhau đối với việc tạo tệp so với việc xoá tệp.

Thao tác read có thể được chia thành getlist.

Một quy tắc write có thể được chia thành create, updatedelete:

service firebase.storage {
  match /b/{bucket}/o {
    // A read rule can be divided into read and list rules
    match /images/{imageId} {
      // Applies to single file read requests
      allow get: if <condition>;
      // Applies to list and listAll requests (Security Rules Version 2)
      allow list: if <condition>;

    // A write rule can be divided into create, update, and delete rules
    match /images/{imageId} {
      // Applies to writes to file contents
      allow create: if <condition>;

      // Applies to updates to (pre-existing) file metadata
      allow update: if <condition>;

      // Applies to delete operations
      allow delete: if <condition>;
    }
  }
 }
}

Câu lệnh match trùng lặp

Tên tệp có thể khớp với nhiều câu lệnh match. Trong trường hợp nhiều biểu thức allow khớp với một yêu cầu, quyền truy cập được cho phép nếu bất kỳ điều kiện nào là true:

service firebase.storage {
  match b/{bucket}/o {
    // Matches file names directly inside of '/images/'.
    match /images/{imageId} {
      allow read, write: if false;
    }

    // Matches file names anywhere under `/images/`
    match /images/{imageId=**} {
      allow read, write: if true;
    }
  }
}

Trong ví dụ trên, tất cả các thao tác đọc và ghi vào các tệp có tên bắt đầu bằng /images/ đều được cho phép vì quy tắc thứ hai luôn là true, ngay cả khi quy tắc đầu tiên là false.

Quy tắc không phải là bộ lọc

Sau khi bảo mật dữ liệu và bắt đầu thực hiện các thao tác trên tệp, hãy lưu ý rằng quy tắc bảo mật không phải là bộ lọc. Bạn không thể thực hiện các thao tác trên một tập hợp tệp khớp với mẫu tên tệp và mong đợi Cloud Storage chỉ truy cập vào các tệp mà ứng dụng hiện tại có quyền truy cập.

Ví dụ: hãy xem quy tắc bảo mật sau:

service firebase.storage {
  match /b/{bucket}/o {
    // Allow the client to read files with contentType 'image/png'
    match /aFileNamePrefix/{aFileName} {
      allow read: if resource.contentType == 'image/png';
    }
  }
}

Bị từ chối: Quy tắc này từ chối yêu cầu sau vì tập hợp kết quả có thể bao gồm các tệp mà contentType không phải là image/png:

Web
filesRef = storage.ref().child("aFilenamePrefix");

filesRef.listAll()
    .then(function(result) {
      console.log("Success: ", result.items);
    })
});

Các quy tắc trong Cloud Storage Security Rules đánh giá từng truy vấn dựa trên kết quả tiềm năng và không thực hiện được yêu cầu nếu có thể trả về một tệp mà ứng dụng không có quyền đọc. Yêu cầu truy cập phải tuân theo các ràng buộc do quy tắc của bạn đặt ra.

Các bước tiếp theo

Bạn có thể hiểu sâu hơn về Firebase Security Rules cho Cloud Storage:

  • Tìm hiểu khái niệm chính tiếp theo của ngôn ngữ Quy tắc, điều kiện động, cho phép Quy tắc kiểm tra quyền của người dùng, so sánh dữ liệu hiện có và dữ liệu đến, xác thực dữ liệu đến và nhiều thao tác khác.

  • Xem xét các trường hợp sử dụng bảo mật điển hình và các Firebase Security Rules định nghĩa giải quyết các trường hợp đó.

Bạn có thể khám phá Firebase Security Rules các trường hợp sử dụng dành riêng cho Cloud Storage: