Tránh các quy tắc không an toàn

Hãy sử dụng hướng dẫn này để tìm hiểu các lỗ hổng thường gặp trong cấu hình Firebase Security Rules, xem xét và bảo mật tốt hơn các quy tắc của riêng bạn, đồng thời kiểm thử các thay đổi trước khi triển khai.

Nếu bạn nhận được cảnh báo rằng dữ liệu của mình không được bảo mật đúng cách, hãy xem lại những lỗi thường gặp này và cập nhật mọi quy tắc dễ bị xâm phạm.

Truy cập vào Firebase Security Rules

Để xem Rules hiện có, hãy sử dụng CLI Firebase hoặc bảng điều khiển Firebase. Hãy nhớ chỉnh sửa các quy tắc của bạn bằng cùng một phương thức, một cách nhất quán để tránh ghi đè nhầm các bản cập nhật. Nếu bạn không chắc chắn rằng các quy tắc do bạn xác định cục bộ có phản ánh các bản cập nhật mới nhất hay không, thì bảng điều khiển Firebase luôn hiển thị phiên bản Firebase Security Rules được triển khai gần đây nhất.

Để truy cập vào các quy tắc của bạn từ bảng điều khiển Firebase, hãy chọn dự án, sau đó chuyển đến Realtime Database, Cloud Firestore hoặc Bộ nhớ. Nhấp vào Quy tắc sau khi bạn đã chuyển đến cơ sở dữ liệu hoặc bộ chứa bộ nhớ chính xác.

Để truy cập vào các quy tắc của bạn từ CLI Firebase, hãy chuyển đến tệp quy tắc được ghi chú trong tệp firebase.json.

Tìm hiểu về Firebase Security Rules

Firebase Security Rules bảo vệ dữ liệu của bạn khỏi người dùng độc hại. Khi tạo một thực thể cơ sở dữ liệu hoặc bộ chứa Cloud Storage trong bảng điều khiển Firebase, bạn có thể chọn từ chối quyền truy cập cho tất cả người dùng (Chế độ khoá) hoặc cấp quyền truy cập cho tất cả người dùng (Chế độ kiểm thử). Mặc dù bạn có thể muốn có cấu hình mở hơn trong quá trình phát triển, nhưng hãy nhớ dành thời gian để định cấu hình đúng cách các quy tắc và bảo mật dữ liệu của bạn trước khi triển khai ứng dụng.

Khi bạn đang phát triển ứng dụng và kiểm thử nhiều cấu hình cho các quy tắc, hãy sử dụng một trong các trình mô phỏng Firebase cục bộ để chạy ứng dụng trong môi trường phát triển cục bộ.

Các trường hợp phổ biến có quy tắc không an toàn

Bạn nên xem xét và cập nhật Rules mà bạn có thể đã thiết lập theo mặc định hoặc khi bạn bắt đầu phát triển ứng dụng trước khi triển khai ứng dụng. Hãy đảm bảo bạn bảo mật đúng cách dữ liệu của người dùng bằng cách tránh những sai lầm thường gặp sau đây.

Truy cập mở

Khi thiết lập dự án Firebase, bạn có thể đã đặt quy tắc cho phép truy cập mở trong quá trình phát triển. Bạn có thể nghĩ rằng bạn là người duy nhất sử dụng ứng dụng của mình, nhưng nếu bạn đã triển khai ứng dụng, thì ứng dụng đó sẽ có trên Internet. Nếu bạn không xác thực người dùng và định cấu hình các quy tắc bảo mật, thì bất kỳ ai đoán được mã dự án của bạn đều có thể đánh cắp, sửa đổi hoặc xoá dữ liệu.

Không nên: Quyền đọc và ghi cho tất cả người dùng.
// Allow read/write access to all users under any conditions
// Warning: **NEVER** use this ruleset in production; it allows
// anyone to overwrite your entire database.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}
{
  // Allow read/write access to all users under any conditions
  // Warning: **NEVER** use this ruleset in production; it allows
  // anyone to overwrite your entire database.

  "rules": {
    ".read": true,
    ".write": true
  }
}
    
// Anyone can read or write to the bucket, even non-users of your app.
// Because it is shared with App Engine, this will also make
// files uploaded using App Engine public.
// Warning: This rule makes every file in your Cloud Storage bucket accessible to any user.
// Apply caution before using it in production, since it means anyone
// can overwrite all your files.

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}
    
Giải pháp: Các quy tắc hạn chế quyền đọc và ghi.

Xây dựng các quy tắc phù hợp với hệ phân cấp dữ liệu của bạn. Một trong những giải pháp phổ biến cho vấn đề bảo mật này là bảo mật dựa trên người dùng bằng Firebase Authentication. Tìm hiểu thêm về cách xác thực người dùng bằng quy tắc.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /some_collection/{document} {
      // Allow reads and deletion if the current user owns the existing document
      allow read, delete: if request.auth.uid == resource.data.author_uid;
      // Allow creation if the current user owns the new document
      allow create: if request.auth.uid == request.resource.data.author_uid;
      // Allow updates by the owner, and prevent change of ownership
      allow update: if request.auth.uid == request.resource.data.author_uid
                    && request.auth.uid == resource.data.author_uid;

    }
  }
}
  
service cloud.firestore {
  match /databases/{database}/documents {
    // Allow public read access, but only content owners can write
    match /some_collection/{document} {
      // Allow public reads
      allow read: if true
      // Allow creation if the current user owns the new document
      allow create: if request.auth.uid == request.resource.data.author_uid;
      // Allow updates by the owner, and prevent change of ownership
      allow update: if request.auth.uid == request.resource.data.author_uid
                    && request.auth.uid == resource.data.author_uid;
      // Allow deletion if the current user owns the existing document
      allow delete: if request.auth.uid == resource.data.author_uid;
    }
  }
}
  
{
  "rules": {
    "some_path": {
      "$uid": {
        // Allow only authenticated content owners access to their data
        ".read": "auth !== null && auth.uid === $uid",
        ".write": "auth !== null && auth.uid === $uid"
      }
    }
  }
}
    
{
  // Allow anyone to read data, but only authenticated content owners can
  // make changes to their data

  "rules": {
    "some_path/$uid": {
      ".read": true,
      // or ".read": "auth.uid !== null" for only authenticated users
      ".write": "auth.uid === $uid"
    }
  }
}
    
// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read;
      allow write: if request.auth.uid == userId;
    }
  }
}
  

Quyền truy cập cho mọi người dùng đã xác thực

Đôi khi, Rules kiểm tra để đảm bảo người dùng đã đăng nhập, nhưng không hạn chế thêm quyền truy cập dựa trên thông tin xác thực đó. Nếu một trong các quy tắc của bạn bao gồm auth != null, hãy xác nhận rằng bạn muốn mọi người dùng đã đăng nhập đều có quyền truy cập vào dữ liệu.

Không nên dùng: Mọi người dùng đã đăng nhập đều có quyền đọc và ghi vào toàn bộ cơ sở dữ liệu của bạn.
service cloud.firestore {
  match /databases/{database}/documents {
    match /some_collection/{document} {
      allow read, write: if request.auth.uid != null;
    }
  }
}
{
  "rules": {
    ".read": "auth.uid !== null",
    ".write": "auth.uid !== null"
  }
}
// Only authenticated users can read or write to the bucket
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}
Giải pháp: Hạn chế quyền truy cập bằng các điều kiện bảo mật.

Khi kiểm tra việc xác thực, bạn cũng nên sử dụng một trong các thuộc tính xác thực để hạn chế thêm quyền truy cập của một số người dùng cụ thể đối với một số tập dữ liệu cụ thể. Tìm hiểu thêm về các thuộc tính xác thực.

service cloud.firestore {
  match /databases/{database}/documents {
    // Assign roles to all users and refine access based on user roles
    match /some_collection/{document} {
     allow read: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Reader"
     allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Writer"

     // Note: Checking for roles in your database using `get` (as in the code
     // above) or `exists` carry standard charges for read operations.
    }
  }
}
// Give each user in your database a particular attribute
// and set it to true/false
// Then, use that attribute to grant access to subsets of data
// For example, an "administrator" attribute set
// to "true" grants write access to data

service cloud.firestore {
  match /databases/{database}/documents {
    match /some_collection/{document} {
      allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
      allow read: true;
    }
  }
}
  
service cloud.firestore {
  match /databases/{database}/documents {
    // Allow public read access, but only content owners can write
    match /some_collection/{document} {
      allow read: if true
      allow write: if request.auth.uid == request.resource.data.author_uid
    }
  }
}
  
{
  "rules": {
    "some_path": {
      "$uid": {
        // Allow only authenticated content owners access to their data
        ".read": "auth.uid === $uid",
        ".write": "auth.uid === $uid"
      }
    }
  }
}
    
{
  "rules": {
    "some_path/$uid": {
      ".write": "auth.uid === $uid",
      // Create a "public" subpath in your dataset
      "public": {
        ".read": true
        // or ".read": "auth.uid !== null"
      },
      // Create a "private" subpath in your dataset
      "private": {
        ".read": "auth.uid === $uid"
      }
    }
  }
}
    
{
  // Allow anyone to read data, but only authenticated content owners can
  // make changes to their data

  "rules": {
    "some_path/$uid": {
      ".read": true,
      // or ".read": "auth.uid !== null" for only authenticated users
      ".write": "auth.uid === $uid"
    }
  }
}
    
// Allow reads if the group ID in your token matches the file metadata `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;
}
// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read;
      allow write: if request.auth.uid == userId;
    }
  }
}
  

(Realtime Database) Quy tắc kế thừa không đúng cách

Loạt Realtime Database Security Rules, với các quy tắc ở các đường dẫn mẹ nông hơn sẽ ghi đè các quy tắc ở các nút con sâu hơn. Khi bạn viết quy tắc tại một nút con, hãy nhớ rằng quy tắc đó chỉ có thể cấp thêm đặc quyền. Bạn không thể tinh chỉnh hoặc thu hồi quyền truy cập vào dữ liệu ở một đường dẫn sâu hơn trong cơ sở dữ liệu.

Không nên: Tinh chỉnh quy tắc tại đường dẫn con
{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          /* ignored, since read was allowed already */
          ".read": false
        }
     }
  }
}
Giải pháp: Viết quy tắc ở các đường dẫn gốc rộng và cấp các đặc quyền cụ thể hơn ở các đường dẫn con. Nếu nhu cầu truy cập dữ liệu của bạn yêu cầu mức độ chi tiết hơn, hãy giữ cho quy tắc của bạn chi tiết. Tìm hiểu thêm về cách xếp chồng Realtime Database Security Rules trong Ngữ pháp cốt lõi của Realtime Database Security Rules.

Đã đóng quyền truy cập

Trong khi phát triển ứng dụng, một phương pháp phổ biến khác là bảo mật dữ liệu. Thông thường, điều này có nghĩa là bạn đã tắt quyền đọc và ghi cho tất cả người dùng như sau:

// Deny read/write access to all users under any conditions
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}
{
  "rules": {
    ".read": false,
    ".write": false
  }
}
    
// Access to files through Cloud Storage is completely disallowed.
// Files may still be accessible through App Engine or Google Cloud Storage APIs.

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

SDK Quản trị Firebase và Cloud Functions vẫn có thể truy cập vào cơ sở dữ liệu của bạn. Hãy sử dụng các quy tắc này khi bạn dự định sử dụng Cloud Firestore hoặc Realtime Database làm phần phụ trợ chỉ dành cho máy chủ kết hợp với SDK quản trị Firebase. Mặc dù ứng dụng này an toàn, nhưng bạn nên kiểm tra để đảm bảo ứng dụng của bạn có thể truy xuất dữ liệu đúng cách.

Tìm hiểu thêm về Cloud Firestore Security Rules và cách hoạt động của các lớp này trong phần Bắt đầu với Cloud Firestore Security Rules.

Kiểm thử Cloud Firestore Security Rules

Để kiểm tra hành vi của ứng dụng và xác minh cấu hình Cloud Firestore Security Rules, hãy sử dụng Trình mô phỏng Firebase. Sử dụng trình mô phỏng Cloud Firestore để chạy và tự động hoá kiểm thử đơn vị trong môi trường cục bộ trước khi triển khai bất kỳ thay đổi nào.

Để nhanh chóng xác thực Firebase Security Rules trong bảng điều khiển Firebase, hãy sử dụng Trình mô phỏng quy tắc Firebase.