Quy tắc bảo mật cơ sở dữ liệu thời gian thực của Firebase cho phép bạn kiểm soát quyền truy cập vào dữ liệu được lưu trữ trong cơ sở dữ liệu của mình. Cú pháp quy tắc linh hoạt cho phép bạn tạo các quy tắc khớp với mọi thứ, từ tất cả các thao tác ghi vào cơ sở dữ liệu của bạn đến các thao tác trên từng nút riêng lẻ.
Quy tắc bảo mật cơ sở dữ liệu thời gian thực là cấu hình khai báo cho cơ sở dữ liệu của bạn. Điều này có nghĩa là các quy tắc được xác định riêng biệt với logic sản phẩm. Điều này có một số lợi thế: khách hàng không chịu trách nhiệm thực thi bảo mật, việc triển khai có lỗi sẽ không làm tổn hại đến dữ liệu của bạn và có lẽ quan trọng nhất là không cần có trọng tài trung gian, chẳng hạn như máy chủ, để bảo vệ dữ liệu khỏi thế giới.
Chủ đề này mô tả cú pháp và cấu trúc cơ bản Các quy tắc bảo mật cơ sở dữ liệu thời gian thực được sử dụng để tạo các bộ quy tắc hoàn chỉnh.
Cấu trúc các quy tắc bảo mật của bạn
Quy tắc bảo mật cơ sở dữ liệu thời gian thực được tạo thành từ các biểu thức giống JavaScript có trong tài liệu JSON. 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.
Các quy tắc cơ bản xác định một tập hợp các nút cần được bảo mật, các phương thức truy cập (ví dụ: đọc, ghi) có liên quan và các điều kiện theo đó quyền truy cập được cho phép hoặc bị từ chối. Trong các ví dụ sau, điều kiện của chúng ta sẽ là các câu lệnh true
và false
đơn giản, nhưng trong chủ đề tiếp theo, chúng ta sẽ đề cập đến những cách linh hoạt hơn để biểu thị điều kiện.
Vì vậy, ví dụ: nếu chúng ta đang cố gắng bảo mật child_node
dưới parent_node
, thì cú pháp chung cần tuân theo là:
{ "rules": { "parent_node": { "child_node": { ".read": <condition>, ".write": <condition>, ".validate": <condition>, } } } }
Hãy áp dụng mô hình này. Ví dụ: giả sử bạn đang theo dõi danh sách thư và 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 được cấu trúc theo cách tương tự. Đây là bộ quy tắc dành cho bảo mật chỉ đọc có thể có ý nghĩa đối với cấu trúc dữ liệu này. Ví dụ này minh họa cách chúng tôi chỉ định các nút cơ sở dữ liệu áp dụng quy tắc nào và các điều kiện để đánh giá quy tắc tại các nút đó.
{ "rules": { // For requests to access the 'messages' node... "messages": { // ...and the individual wildcarded 'message' nodes beneath // (we'll cover wildcarding variables more a bit later).... "$message": { // For each message, allow a read operation if <condition>. In this // case, we specify our condition as "true", so read access is always granted. ".read": "true", // For read-only behavior, we specify that for write operations, our // condition is false. ".write": "false" } } } }
Quy tắc hoạt động cơ bản
Có ba loại quy tắc để thực thi bảo mật dựa trên loại hoạt động được thực hiện trên dữ liệu: .write
, .read
và .validate
. Dưới đây là tóm tắt nhanh về mục đích của họ:
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. |
Biến chụp ký tự đại diện
Tất cả các câu lệnh quy tắc đều trỏ đến các nút. Một câu lệnh có thể trỏ đến một nút cụ thể hoặc sử dụng các biến chụp ký tự đại diện $
để trỏ đến các tập hợp nút ở cấp độ phân cấp. Sử dụng các biến chụp này để lưu trữ giá trị của các khóa nút để sử dụng bên trong các câu lệnh quy tắc tiếp theo. Kỹ thuật này cho phép bạn viết các điều kiện Quy tắc phức tạp hơn, điều mà chúng tôi sẽ đề cập chi tiết hơn trong chủ đề tiếp theo.
{ "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')" } } } } }
Các biến $
động cũng có thể được sử dụng song song với các tên đường dẫn không đổi. Trong ví dụ này, chúng tôi đang sử dụng biến $other
để khai báo quy tắc .validate
nhằm đảm bảo rằng widget
không có con nào khác ngoài title
và color
. Bất kỳ thao tác viết nào dẫn đến việc tạo thêm trẻ em đều sẽ thất bạ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 } } } }
Xếp tầng các quy tắc đọc và ghi
Các quy tắc .read
và .write
hoạt động từ trên xuống, với các quy tắc nông hơn sẽ ghi đè các quy tắc sâu hơn. Nếu một quy tắc cấp quyền đọc hoặc ghi trên một đường dẫn cụ thể thì quy tắc đó cũng cấp quyền truy cập vào tất cả các nút con bên dưới nó. Hãy xem xét cấu trúc sau:
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } } }
Cấu trúc bảo mật này cho phép /bar/
được đọc từ bất cứ khi nào /foo/
chứa baz
con có giá trị true
. Quy tắc ".read": false
trong /foo/bar/
không có hiệu lực ở đây, vì đường dẫn con không thể thu hồi quyền truy cập.
Mặc dù nó có vẻ không trực quan ngay lập tức nhưng đây là một phần mạnh mẽ của ngôn ngữ quy tắc và cho phép thực hiện các đặc quyền truy cập rất phức tạp với nỗ lực tối thiểu. Điều này sẽ được minh họa khi chúng ta đề cập đến vấn đề bảo mật dựa trên người dùng ở phần sau của hướng dẫn này.
Lưu ý rằng các quy tắc .validate
không xếp tầng. Tất cả các quy tắc xác thực phải được đáp ứng ở tất cả các cấp độ của hệ thống phân cấp để được phép ghi.
Quy tắc không phải là bộ lọc
Các quy tắc được áp dụng theo cách nguyên tử. Điều đó có nghĩa là thao tác đọc hoặc ghi sẽ không thành công ngay lập tức nếu không có quy tắc tại vị trí đó hoặc tại vị trí chính cấp quyền truy cập. Ngay cả khi mọi đường dẫn con bị ảnh hưởng đều có thể truy cập được thì việc đọc ở vị trí gốc sẽ hoàn toàn không thành công. Hãy xem xét cấu trúc này:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
Nếu không hiểu rằng các quy tắc được đánh giá nguyên tử, có vẻ như việc tìm nạp đường dẫn /records/
sẽ trả về rec1
chứ không phải rec2
. Tuy nhiên, kết quả thực tế là một lỗi:
JavaScript
var db = firebase.database(); db.ref("records").once("value", function(snap) { // success method is not called }, function(err) { // error callback triggered with PERMISSION_DENIED });
Mục tiêu-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // success block is not called } withCancelBlock:^(NSError * _Nonnull error) { // cancel block triggered with PERMISSION_DENIED }];
Nhanh
var ref = FIRDatabase.database().reference() ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in // success block is not called }, withCancelBlock: { error in // cancel block triggered with PERMISSION_DENIED })
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // success method is not called } @Override public void onCancelled(FirebaseError firebaseError) { // error callback triggered with PERMISSION_DENIED }); });
NGHỈ NGƠI
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
Vì thao tác đọc tại /records/
là nguyên tử và không có quy tắc đọc nào cấp quyền truy cập vào tất cả dữ liệu trong /records/
, điều này sẽ gây ra lỗi PERMISSION_DENIED
. Nếu đánh giá quy tắc này trong trình mô phỏng bảo mật trong bảng điều khiển Firebase , chúng tôi có thể thấy rằng thao tác đọc đã bị từ chối vì không có quy tắc đọc nào cho phép truy cập vào đường dẫn /records/
. Tuy nhiên, hãy lưu ý rằng quy tắc cho rec1
chưa bao giờ được đánh giá vì nó không nằm trong đường dẫn mà chúng tôi yêu cầu. Để tìm nạp rec1
, chúng ta cần truy cập trực tiếp vào nó:
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
Mục tiêu-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Nhanh
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records/rec1"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // SUCCESS! } @Override public void onCancelled(FirebaseError firebaseError) { // error callback is not called } });
NGHỈ NGƠI
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
Tuyên bố chồng chéo
Có thể áp dụng nhiều quy tắc cho một nút. Trong trường hợp nhiều biểu thức quy tắc xác định một nút, phương thức truy cập sẽ bị từ chối nếu bất kỳ điều kiện nào là false
:
{ "rules": { "messages": { // A rule expression that applies to all nodes in the 'messages' node "$message": { ".read": "true", ".write": "true" }, // A second rule expression applying specifically to the 'message1` node "message1": { ".read": "false", ".write": "false" } } } }
Trong ví dụ trên, việc đọc nút message1
sẽ bị từ chối vì quy tắc thứ hai luôn false
, mặc dù quy tắc đầu tiên luôn true
.
Bước tiếp theo
Bạn có thể hiểu sâu hơn về Quy tắc bảo mật cơ sở dữ liệu thời gian thực của Firebase:
Tìm hiểu khái niệm chính tiếp theo về ngôn ngữ Quy tắc, điều kiện động, cho phép Quy tắc của bạn kiểm tra ủy quyền 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, kiểm tra cấu trúc truy vấn đến từ máy khách, v.v.
Xem lại các trường hợp sử dụng bảo mật điển hình và định nghĩa Quy tắc bảo mật Firebase giải quyết các trường hợp đó .