使用安全规则限制子/字段访问

我正在编写一个应用程序,允许用户提交在被显示给其他用户之前进行审核的提名。 这要求我在实施安全规则方面迄今尚未成功的一些限制:

  1. 隐藏任何尚未获得批准的提名
  2. 隐藏提交的私人字段(电话,审批状态,创builddate等)

我目前的规则如下:

{ "rules": { "nominations": { ".read": true, "$nominationId": { ".read": "data.child('state').val() == 'approved' || auth != null", // Only read approved nominations if not authenticated ".write": "!data.exists()", // Only allow new nominations to be created "phone": { ".read": "auth != null" // Only allow authenticated users to read phone number }, "state": { ".read": "auth != null", // Only allow authenticated users to read approval state ".write": "auth != null" // Only allow authenticated users to change state } } } } } 

儿童规则(如$nomination )不妨碍整个孩子从父母那里读取。 如果我在https://my.firebaseio.com/nominations上收听child_added ,即使上述安全规则已经到位,它child_added愉快地返回所有孩子及其所有的数据。

我目前的解决方法是保留一个名为approved的单独节点,只要有人批准或拒绝提名就可以在列表之间移动数据,但这似乎是一个非常糟糕的方法。

更新

继迈克尔·勒恩鲍尔 ( Michael Lehenbauer )的出色评论之后,我用最小的努力重新实现了最初的想法。

新的数据结构如下:

 my-firebase | `- nominations | `- entries | | | `- private | `- public | `- status | `- pending `- approved `- rejected 

每个提名都存储在私人数据entries下,如电话号码,电子邮件等, private和公众可查看的数据下。

更新的规则如下:

 { "rules": { "nominations": { "entries": { "$id": { ".write": "!data.exists()", "public": { ".read": true, }, "private": { ".read": "auth != null" } } }, "status": { "pending": { ".read": "auth != null", "$id": { ".write": "root.child('nominations/entries').child($id).exists() && (auth != null || newData.val() == true)" } }, "approved": { ".read": true, "$id": { ".write": "root.child('nominations/entries').child($id).exists() && auth != null" } }, "rejected": { ".read": "auth != null", "$id": { ".write": "root.child('nominations/entries').child($id).exists() && auth != null" } } } } } } 

和JavaScript的实现:

 var db = new Firebase('https://my.firebaseio.com') var nominations = db.child('nominations') var entries = nominations.child('entries') var status = nominations.child('status') var pending = status.child('pending') var approved = status.child('approved') var rejected = status.child('rejected') // Create nomination via form input (not shown) var createNomination = function() { var data = { public: { name: 'Foo', age: 20 }, private: { createdAt: new Date().getTime(), phone: 123456 } } var nomination = entries.push() nomination.setWithPriority(data, data.private.createdAt) pending.child(nomination.name()).set(true) } // Retrieve current nomination status var getStatus = function(id, callback) { approved.child(id).once('value', function(snapshot) { if (snapshot.val()) { callback(id, 'approved') } else { rejected.child(id).once('value', function(snapshot) { callback(id, snapshot.val() ? 'rejected' : 'pending') }) } }) } // Change status of nomination var changeStatus = function(id, from, to) { status.child(from).child(id).remove() status.child(to).child(id).set(true) } 

我正在努力实现的唯一部分是处理状态变化,我当前的方法肯定可以改进:

 _.each([pending, approved, rejected], function(status) { status.on('child_added', function(snapshot) { $('#' + snapshot.name()).removeClass('pending approved rejected').addClass(status.name()) }) }) 

我打算在nominations/status上使用child_changed ,但是我一直无法可靠地运作。

加藤的权利。 理解安全规则从不过滤数据是很重要的。 对于任何位置,您都可以读取所有数据(包括其子项),或者不读取所有数据。 所以就你的规则而言,在“提名”下有一个“.read”:true是否定所有其他规则。

所以我在这里推荐的方法是有3个列表。 一份载有提名资料,一份载列获批准提名名单,一份载列未决提名名单。

你的规则可能是这样的:

 { "rules": { // The actual nominations. Each will be stored with a unique ID. "nominations": { "$id": { ".write": "!data.exists()", // anybody can create new nominations, but not overwrite existing ones. "public_data": { ".read": true // everybody can read the public data. }, "phone": { ".read": "auth != null", // only authenticated users can read the phone number. } } }, "approved_list": { ".read": true, // everybody can read the approved nominations list. "$id": { // Authenticated users can add the id of a nomination to the approved list // by creating a child with the nomination id as the name and true as the value. ".write": "auth != null && root.child('nominations').child($id).exists() && newData.val() == true" } }, "pending_list": { ".read": "auth != null", // Only authenticated users can read the pending list. "$id": { // Any user can add a nomination to the pending list, to be moderated by // an authenticated user (who can then delete it from this list). ".write": "root.child('nominations').child($id).exists() && (newData.val() == true || auth != null)" } } } } 

未经身份validation的用户可以添加新的提名:

 var id = ref.child('nominations').push({ public_data: "whatever", phone: "555-1234" }); ref.child('pending_list').child(id).set(true); 

经过身份validation的用户可以通过以下方式批准邮件:

 ref.child('pending_list').child(id).remove(); ref.child('approved_list').child(id).set(true); 

为了渲染已批准和待处理的列表,您可以使用如下代码:

 ref.child('approved_list').on('child_added', function(childSnapshot) { var nominationId = childSnapshot.name(); ref.child('nominations').child(nominationId).child('public_data').on('value', function(nominationDataSnap) { console.log(nominationDataSnap.val()); }); }); 

通过这种方式,您可以使用approved_list和pending_list作为轻量级列表(可以分别通过未经身份validation的用户身份validation)并将所有实际提名数据存储在提名列表中(任何人都不能直接枚举)。

如果我完全理解安全规则的工作方式(我只是自己学习),那么当任何一个规则允许访问时,授予访问权限。 因此,它们被读作如下:

  • 提名“.read”:true,ACCESS GRANTED
  • 其他规则:不读

此外,如果该规则被删除, $nominationId “.read”授予访问权限,如果logging被批准; 因此,只要获得批准, phonestate就会变得多余。

将其分解为public/ private/儿童可能是最简单的,如下所示:

 nominations/unapproved/ # only visible to logged in users nominations/approved/ # visible to anyone (move record here after approval) nominations/approved/public/ # things everyone can see nominations/approved/restricted/ # things like phone number, which are restricted 

UPDATE

考虑到这一点,我认为你仍然会遇到一个approved/公开的问题,这将允许你列出logging,并已approved/restricted/私人。 在这个用例中,受限制的数据也可能需要自己的path。

这个线程是有点过时了,它可能通过规则有一个解决scheme,但作为video说,它是一个巧妙的把戏: https : //youtu.be/5hYMDfDoHpI?t=8m50s

这可能不是一个好的做法,因为firebase文档说规则不是filter: https : //firebase.google.com/docs/database/security/securing-data

我不是安全专家,但我testing了这个技巧,对我来说工作得很好。 🙂

所以我希望更好地了解这个实现的安全问题。

安全规则的问题:

  1. JSON的结构或者你想创build的数据库是如此多的所谓的安全规则; 它看起来不可思议,感觉怪异; 而不是根据需要来构造数据,更陡峭的学习曲线用于构造安全规则并将数据库从各处移动到各个部分,这可能只是一个简单的JSON数组字段。 从Mongo根据您的需要和过滤发布远远好于Firebase。 为什么不Firebase做到这一点?
  2. 安全规则文档很糟糕。 一切都因为缺乏实例而陷入困境。 应该提供JSON对象或数据库示例的明确示例。 到处都是安全规则不是filter。 那个陈述太模糊了。 为什么这么一个class轮文件? Firebase必须以更简单的方式更新文档,以便全面了解或忽略安全规则。 他们迫切想要做到这一点。