Firebase 使用安全规则限制子/字段访问
我正在编写一个应用程序,允许用户提交提名,提名在显示给其他用户之前经过审核。这需要一些限制,我迄今为止在安全规则方面没有成功实施: 隐藏任何尚未批准的提名 从提交电话、批准状态、创建日期等隐藏私人字段。 我目前的规则如下:Firebase 使用安全规则限制子/字段访问,firebase,firebase-realtime-database,Firebase,Firebase Realtime Database,我正在编写一个应用程序,允许用户提交提名,提名在显示给其他用户之前经过审核。这需要一些限制,我迄今为止在安全规则方面没有成功实施: 隐藏任何尚未批准的提名 从提交电话、批准状态、创建日期等隐藏私人字段。 我目前的规则如下: { "rules": { "nominations": { ".read": true, "$nominationId": { ".read": "data.child(
{
"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
}
}
}
}
}
my-firebase
|
`- nominations
|
`- entries
| |
| `- private
| `- public
|
`- status
|
`- pending
`- approved
`- rejected
儿童规则,例如$提名,不会阻止家长阅读整个儿童。如果我听了child_的话,它会愉快地返回所有的孩子和他们的所有数据,即使有上述安全规则
我目前的解决方案是保留一个名为approved的单独节点,并在有人批准或拒绝提名时在列表之间移动数据,但这似乎是一种非常糟糕的方法
更新
下面是我的精彩评论,我用最少的努力重新实现了最初的想法
新的数据结构如下:
{
"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
}
}
}
}
}
my-firebase
|
`- nominations
|
`- entries
| |
| `- private
| `- public
|
`- status
|
`- pending
`- approved
`- rejected
每个提名都存储在带有私人数据(如电话号码、电子邮件等)的条目下。在“公共”下的“私人”和“公共”下的“可公开查看的数据”下
更新后的规则如下:
{
"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
}
}
}
}
}
my-firebase
|
`- nominations
|
`- entries
| |
| `- private
| `- public
|
`- status
|
`- pending
`- approved
`- rejected
{
规则:{
提名:{
参赛作品:{
$id:{
.write:!data.exists,
公众:{
读:是的,
},
私人:{
.read:auth!=null
}
}
},
地位:{
待定:{
.read:auth!=null,
$id:{
.write:root.child“提名/条目”.child$id.exists&&auth!=null | | newData.val==true
}
},
核准:{
读:是的,
$id:{
.write:root.child“提名/条目”。child$id.exists&&auth!=null
}
},
拒绝:{
.read:auth!=null,
$id:{
.write:root.child“提名/条目”。child$id.exists&&auth!=null
}
}
}
}
}
}
以及JavaScript实现:
var db=新Firebase'https://my.firebaseio.com'
var Naminations=db.child'Naminations'
变量条目=提名。子项“条目”
变量状态=提名。子“状态”
var pending=status.child'pending'
var已批准=状态。子级“已批准”
var rejected=状态。子级“rejected”
//通过未显示的表单输入创建提名
var=函数{
风险值数据={
公众:{
名称:“Foo”,
年龄:20
},
私人:{
createdAt:new Date.getTime,
电话:123456
}
}
var提名=条目。推送
提名.setWithPrioritydata,data.private.createdAt
pending.childnamification.name.settrue
}
//检索当前提名状态
var getStatus=functionid,回调{
approved.childid.once'value',functionsnapshot{
如果snapshot.val{
callbackid,“已批准”
}否则{
拒绝。childid。一旦“值”,functionsnapshot{
callbackid,snapshot.val?“已拒绝”:“挂起”
}
}
}
}
//更改提名状态
var changeStatus=函数ID,从,到{
status.childfrom.childid.remove
status.childto.childid.settrue
}
我正在努力解决的唯一一个实现部分是处理状态更改,我当前的方法肯定可以改进:
_.每个[待定、批准、拒绝]功能状态{
状态。在'child_added'上,函数快照{
$+snapshot.name.removeClass“待定已批准已拒绝”。addClassstatus.name
}
}
我曾计划使用child_changed on Namination/status,但我无法让它可靠地工作。如果我完全摸索安全规则的工作方式,我只是自己学习它们,那么当任何一条规则允许访问时,访问都被授予。因此,其内容如下:
{
"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
}
}
}
}
}
my-firebase
|
`- nominations
|
`- entries
| |
| `- private
| `- public
|
`- status
|
`- pending
`- approved
`- rejected
提名。改为:正确,授予访问权限
其他规则:不要阅读
此外,如果删除了该规则,$namignionId.read将在记录被批准时授予访问权限;因此,只要得到批准,.read-in-phone和state就变得多余了
将其分解为公共/和私人/儿童可能是最简单的,如下所示:
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
更新
再想一想,我认为您仍然会遇到一个问题,即将批准/公开,这将允许您列出记录,以及批准/限制/私有。在这个用例中,受限制的数据可能也需要它自己的路径。加藤是对的。重要的是要了解安全规则从不过滤数据。对于任何位置,您都可以阅读所有 包含或不包含其子项的数据。因此,在你的规则中,在提名项下有一个.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)"
}
}
}
}
未经身份验证的用户可以添加具有以下内容的新提名:
var id = ref.child('nominations').push({ public_data: "whatever", phone: "555-1234" });
ref.child('pending_list').child(id).set(true);
经过身份验证的用户可以通过以下方式批准邮件:
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作为轻量级列表,未经身份验证和身份验证的用户可以分别枚举这些列表,并将所有实际提名数据存储在提名列表中,没有人可以直接枚举。此线程有点过时,它可能有一个通过规则的解决方案,但正如视频所说,这是一个巧妙的技巧: 这可能不是一个好的做法,因为firebase文档说规则不是过滤器: 我不是安全方面的专家,但我测试了这个技巧,它对我很有效
因此,我希望能够更好地理解此实现中的安全问题。在我看来,对于任何需要过滤或隐藏最终用户信息的应用程序,我都会遇到类似的问题,坦率地说,您概述的方法只能证明我可能应该重新考虑在有需求的项目中使用Firebase像这样,很公平。虽然我会记住几件事。首先,像这样对数据进行非规范化一开始可能会感到尴尬,但这是一种完全有效的方法。例如,当您查看twitter提要时,twitter不会进行任何过滤。这都是预先计算好的。这会占用额外的空间,并可能导致暂时不一致的数据,但它的伸缩性比加入/过滤数据要好得多。第二,您需要的基本上是每行访问控制,我认为这在任何数据库中都很难找到。如果我能提供任何帮助,请随时联系firebase.com的michael!举一个@SimenBrekken point的例子:聊天室希望允许用户查看在joinDate和leaveDate之间发送的消息。我们可以在查询中进行过滤以显示,但最终用户仍然可以访问他们不应该看到的消息。由于所有日期都是动态的,因此我们无法进入已批准的_列表、待定的_列表等@michael lehenbauer,是否可以使用Firebase功能来保护数据访问控制?我看不到来自FB的onReaddoc@MichaelLehenbauer第二,您需要的基本上是每行访问控制,我认为这在任何数据库中都很难找到。具有讽刺意味的是,这在许多数据库中通常很难实现或不完整,因为用户登录并不总是将1:1映射到数据库登录。用firebase解决了这个问题。简单的一点应该是只显示./user\u id字段与登录用户匹配的记录,或者只显示登录用户的组有权访问该记录的记录。最好将答案放在这里,而不是一个可能会死掉的链接…上面的视频不违反规则不过滤语句-即,这不是一个试图绕过这一点的把戏。这只是构建数据的一种方法,可以有效地与规则系统配合使用。