Firebase 数据库快速入门处理计数的方式是否安全?
Is the way the Firebase database quickstart handles counts secure?
我想为文章点赞创建一个增量字段。
我指的是这个link:https://firebase.google.com/docs/database/android/save-data#save_data_as_transactions
示例中有增量字段的代码:
if (p.stars.containsKey(getUid())) {
// Unstar the post and remove self from stars
p.starCount = p.starCount - 1;
p.stars.remove(getUid());
} else {
// Star the post and add self to stars
p.starCount = p.starCount + 1;
p.stars.put(getUid(), true);
}
但是我如何确定用户是否已经 liked/unliked 文章?
在这个例子中,用户(黑客)不妨像这样清除整个星图,它仍然会保存:
p.stars = new HashMap<>();
这会破坏其他已经喜欢它的用户的逻辑。
我什至不认为你可以为此制定规则,尤其是 "decrease count" 行动。
有什么帮助和建议吗?
安全规则可以做几件事:
确保一个用户只能add/remove自己uid
到stars
节点
"stars": {
"$uid": {
".write": "$uid == auth.uid"
}
}
确保用户只有在将自己的 uid
添加到 stars
节点或从那里删除它时才能更改 starCount
- 确保用户只能increase/decrease
starCount
by 1
即使有了这些,拥有确保 starCount
等于 stars
节点中的 uid 数量的安全规则可能确实仍然很棘手。不过,我鼓励您尝试一下,并分享您的结果。
虽然我看到大多数开发人员处理此问题的方式是:
- 在客户端做开始计数(如果
stars
节点的大小不是太大,这是合理的)。
- 在将
stars
聚合到 starCount
的服务器上有一个受信任的进程 运行。它可以为 incrementing/decrementing. 使用 child_added/child_removed 事件
更新:带有工作示例
我写了一个投票系统的工作示例。数据结构为:
votes: {
uid1: true,
uid2: true,
},
voteCount: 2
当用户投票时,应用会发送多位置更新:
{
"/votes/uid3": true,
"voteCount": 3
}
然后删除他们的投票:
{
"/votes/uid3": null,
"voteCount": 2
}
这意味着应用需要显式读取 voteCount
的当前值,其中:
function vote(auth) {
ref.child('voteCount').once('value', function(voteCount) {
var updates = {};
updates['votes/'+auth.uid] = true;
updates.voteCount = voteCount.val() + 1;
ref.update(updates);
});
}
它本质上是一个多地点交易,但是内置了应用程序代码和安全规则,而不是 Firebase SDK 和服务器本身。
安全规则做了一些事情:
- 确保voteCount只能增加或减少1
- 确保用户只能add/remove他们自己的投票
- 确保计数增加伴随着投票
- 确保计数减少伴随着 "unvote"
- 确保投票伴随着计数增加
注意规则没有:
- 确保 "unvote" 伴随着计数减少(可以使用
.write
规则来完成)
- 重试失败votes/unvotes(处理并发voting/unvoting)
规则:
"votes": {
"$uid": {
".write": "auth.uid == $uid",
".validate": "(!data.exists() && newData.val() == true &&
newData.parent().parent().child('voteCount').val() == data.parent().parent().child('voteCount').val() + 1
)"
}
},
"voteCount": {
".validate": "(newData.val() == data.val() + 1 &&
newData.parent().child('votes').child(auth.uid).val() == true &&
!data.parent().child('votes').child(auth.uid).exists()
) ||
(newData.val() == data.val() - 1 &&
!newData.parent().child('votes').child(auth.uid).exists() &&
data.parent().child('votes').child(auth.uid).val() == true
)",
".write": "auth != null"
}
jsbin 用一些代码来测试这个:http://jsbin.com/yaxexe/edit?js,console
我想为文章点赞创建一个增量字段。
我指的是这个link:https://firebase.google.com/docs/database/android/save-data#save_data_as_transactions
示例中有增量字段的代码:
if (p.stars.containsKey(getUid())) {
// Unstar the post and remove self from stars
p.starCount = p.starCount - 1;
p.stars.remove(getUid());
} else {
// Star the post and add self to stars
p.starCount = p.starCount + 1;
p.stars.put(getUid(), true);
}
但是我如何确定用户是否已经 liked/unliked 文章?
在这个例子中,用户(黑客)不妨像这样清除整个星图,它仍然会保存:
p.stars = new HashMap<>();
这会破坏其他已经喜欢它的用户的逻辑。
我什至不认为你可以为此制定规则,尤其是 "decrease count" 行动。
有什么帮助和建议吗?
安全规则可以做几件事:
确保一个用户只能add/remove自己
uid
到stars
节点"stars": { "$uid": { ".write": "$uid == auth.uid" } }
确保用户只有在将自己的
uid
添加到stars
节点或从那里删除它时才能更改starCount
- 确保用户只能increase/decrease
starCount
by 1
即使有了这些,拥有确保 starCount
等于 stars
节点中的 uid 数量的安全规则可能确实仍然很棘手。不过,我鼓励您尝试一下,并分享您的结果。
虽然我看到大多数开发人员处理此问题的方式是:
- 在客户端做开始计数(如果
stars
节点的大小不是太大,这是合理的)。 - 在将
stars
聚合到starCount
的服务器上有一个受信任的进程 运行。它可以为 incrementing/decrementing. 使用 child_added/child_removed 事件
更新:带有工作示例
我写了一个投票系统的工作示例。数据结构为:
votes: {
uid1: true,
uid2: true,
},
voteCount: 2
当用户投票时,应用会发送多位置更新:
{
"/votes/uid3": true,
"voteCount": 3
}
然后删除他们的投票:
{
"/votes/uid3": null,
"voteCount": 2
}
这意味着应用需要显式读取 voteCount
的当前值,其中:
function vote(auth) {
ref.child('voteCount').once('value', function(voteCount) {
var updates = {};
updates['votes/'+auth.uid] = true;
updates.voteCount = voteCount.val() + 1;
ref.update(updates);
});
}
它本质上是一个多地点交易,但是内置了应用程序代码和安全规则,而不是 Firebase SDK 和服务器本身。
安全规则做了一些事情:
- 确保voteCount只能增加或减少1
- 确保用户只能add/remove他们自己的投票
- 确保计数增加伴随着投票
- 确保计数减少伴随着 "unvote"
- 确保投票伴随着计数增加
注意规则没有:
- 确保 "unvote" 伴随着计数减少(可以使用
.write
规则来完成) - 重试失败votes/unvotes(处理并发voting/unvoting)
规则:
"votes": {
"$uid": {
".write": "auth.uid == $uid",
".validate": "(!data.exists() && newData.val() == true &&
newData.parent().parent().child('voteCount').val() == data.parent().parent().child('voteCount').val() + 1
)"
}
},
"voteCount": {
".validate": "(newData.val() == data.val() + 1 &&
newData.parent().child('votes').child(auth.uid).val() == true &&
!data.parent().child('votes').child(auth.uid).exists()
) ||
(newData.val() == data.val() - 1 &&
!newData.parent().child('votes').child(auth.uid).exists() &&
data.parent().child('votes').child(auth.uid).val() == true
)",
".write": "auth != null"
}
jsbin 用一些代码来测试这个:http://jsbin.com/yaxexe/edit?js,console