Fra*_*len 9 google-cloud-firestore firebase-security-rules
我有一个使用Firebase SDK直接从应用程序内部与Cloud Firestore对话的应用程序。我的代码确保仅以合理的间隔写入数据。但是恶意用户可能会从我的应用程序中获取配置数据,并使用它将无尽的数据流写入我的数据库。
我如何确保用户每隔几秒钟只能写一次发言,而不必编写任何服务器端代码。
Fra*_*len 17
对数据库的每次读或写操作都将根据您为项目配置的安全规则在Google的服务器上进行验证。这些规则只能由项目的协作者设置,但适用于访问项目中数据库的所有客户端代码。这意味着您可以在这些安全规则中强制执行此条件,即使恶意用户也无法绕过它们,因为它们无权访问您的项目。
假设我们有一个users集合,并且那里的每个文档都有一个带有用户UID的ID。这些安全规则确保用户只能写自己的文档,并且每5秒只能写一次:
match /users/{document=**} {
allow create: if isMine() && hasTimestamp();
allow update: if isMine() && hasTimestamp() && isCalm();
function isMine() {
return request.resource.id == request.auth.uid;
}
function hasTimestamp() {
return request.resource.data.timestamp == request.time;
}
function isCalm() {
return request.time > resource.data.timestamp + duration.value(5, 's');
}
}
Run Code Online (Sandbox Code Playgroud)
演练可能会有所帮助:
第一行确定其中规则的范围,因此这些规则适用于/users集合中的所有文档。
用户可以创建自己的文档(isMine()),如果带有时间戳(hasTimestamp()),则可以创建该文档。
用户可以更新文档(如果有的话),带有时间戳记并且不经常写(isCalm())。
让我们依次看一下这三个功能...
该isMine()功能检查文档ID是否与执行写操作的用户相同。由于auth.uidFirebase是根据登录的用户自动填充的,因此恶意用户无法欺骗该值。
该hasTimestamp()函数检查正在写入的文档(request.resource)是否具有时间戳字段,如果有,则该时间戳是否与当前服务器端时间相同。这意味着在代码中,需要指定FieldValue.serverTimestamp()才能使写入可接受。因此,您只能编写当前的服务器端时间戳,而恶意用户则无法传递其他时间戳。
该isCalm()功能确保用户不会经常写作。如果timestamp现有文档(resource.data.timestamp)和request.resource.data.timestamp当前正在写入的文档()中的值之间的差异至少为5秒,则允许写入。
Per Doug的评论:
重要的是要注意,以上实现的是每个文档的写限制,而不是每个帐户的写限制。用户仍然可以自由地以系统允许的速度编写其他文档。
如果要对他们写的所有文档有按用户的写速率限制,请继续阅读。
这是我测试这些规则的方式的jsbin:https ://jsbin.com/kejobej/2/edit?js,console 。使用此代码:
firebase.auth().signInAnonymously().then(function(auth) {
var doc = collection.doc(auth.user.uid);
doc.set({
timestamp: firebase.firestore.FieldValue.serverTimestamp()
}).then(function() {
console.log("Written at "+new Date());
}).catch(function(error) {
console.error(error.code);
})
})
Run Code Online (Sandbox Code Playgroud)
如果您反复单击该Run按钮,则仅在自上一次写入后至少经过5秒后才允许下一次写入。
当我大约每秒单击一次“运行”按钮时,我得到:
“写在2019年6月6日星期四20:20:19 GMT-0700(太平洋夏令时间)”
“没有权限”
“没有权限”
“没有权限”
“没有权限”
“写在2019年6月6日星期四20:20:24 GMT-0700(太平洋夏令时间)”
“没有权限”
“没有权限”
“没有权限”
“没有权限”
“写于2019年6月6日星期四20:20:30 GMT-0700(太平洋夏令时间)”
最后一个示例是每个用户的写入速率限制。假设您有一个社交媒体应用程序,用户在其中创建帖子,每个用户都有一个个人资料。因此,我们有两个集合:posts和users。并且我们希望确保用户最多每5秒创建一次新帖子。
规则与以前几乎相同,如下所示:用户可以更新自己的个人资料,如果过去5秒钟内没有写任何帖子,则可以创建一个帖子。
最大的不同是/users/$uid,即使他们正在创建新的帖子文档(/posts/$newid),我们也将时间戳存储在他们的用户个人资料()中。由于这两次写操作必须合而为一,因此我们将使用BatchedWrite一次遍历:
var root = firebase.firestore();
var users = root.collection("users");
var posts = root.collection("posts");
firebase.auth().signInAnonymously().then(function(auth) {
var batch = db.batch();
var userDoc = users.doc(auth.user.uid);
batch.set(userDoc, {
timestamp: firebase.firestore.FieldValue.serverTimestamp()
})
batch.set(posts.doc(), {
title: "Hello world"
});
batch.commit().then(function() {
console.log("Written at "+new Date());
}).catch(function(error) {
console.error(error.code);
})
})
Run Code Online (Sandbox Code Playgroud)
因此,该批处理写了两件事:
如前所述,用于此目的的顶级安全规则与以前几乎相同:
match /users/{user} {
allow write: if isMine() && hasTimestamp();
}
match /posts/{post} {
allow write: if isCalm();
}
Run Code Online (Sandbox Code Playgroud)
因此,用户可以写入个人资料文档(如果该文档是自己的),并且该文档包含等于当前服务器端/请求时间的时间戳。如果用户最近没有发布过帖子,则可以写一个帖子。
和的实现isMine()与hasTimstamp()以前相同。但是isCalm()now 的实现现在在写操作之前和之后查找用户概要文件,以进行时间戳检查:
function isCalm() {
return getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.timestamp
> get(/databases/$(database)/documents/users/$(request.auth.uid)).data.timestamp + duration.value(5, 's');
}
Run Code Online (Sandbox Code Playgroud)
到PATH get()和getAfter()遗憾的是必须是绝对的,完全合格的,但它归结为:
// These won't work, but are easier to read.
function isCalm() {
return getAfter(/users/$(request.auth.uid)).data.timestamp
> get(/users/$(request.auth.uid)).data.timestamp + duration.value(5, 's');
}
Run Code Online (Sandbox Code Playgroud)
注意事项: