Firebase Firestore - 限制 product-documents 用户可以创建的数量
Firebase Firestore - Limit the number of product-documents users can create
我有一个 Angular web-app,使用 Firebase 作为无服务器后端技术。 web-app 当前的目的是让注册用户为提供的产品创建文档。虽然企业用户可以创建无限 product-docs,但免费用户应该只能创建一个 product-doc。
Collections:
- 用户 -
{id: string, isBusiness: boolean, name: string, email: string, productIds: string[]}
- 产品 -
{id: string, title: string, description: string, userId: string}
每当用户使用
创建新的 product-doc
public createProduct(id: string, product: Product) {
return this.afStore.collection('products').doc(id).set(product, { merge: true });
}
我运行一个带有触发器onCreate
的云函数将创建的product-doc的id
添加到相应[的productIds
字段=45=]。这允许我检查 frontend-side if(!user.isBusiness && productIds.length > 1)
以限制为免费用户帐户创建多个 product-docs。
然而,我最近做了一个 pen-test 并且测试人员偶然发现,如果您在多个浏览器选项卡上打开网站并同时 运行 每个选项卡上的 createProduct()
功能同时,所有这些都将执行,允许非业务用户拥有多个 product-docs.
有没有人处理过类似的情况?我不知道是否有办法通过 Firestore 安全规则避免这种情况,或者是否需要等待活动事务完成。
在用户中创建一个 productCounter
节点。然后将您的规则与以下内容合并:
{
"rules": {
"cascade_your_nodes": {
"products": {
"$productId" : {
.".write": "root.child('YOUR_PATHS').child('users').child(auth.uid).child('productCounter').val() < 1 || data.exists() || root.child('YOUR_PATHS').child('users').child(auth.uid).child('isBusiness').val()"
}
}
}
}
}
推理:
如果它不与任何现有规则冲突,则仅在以下情况下才允许写入任何 productId
:
请求uid
节点有productCounter
小于1
root.child('YOUR_PATHS').child('users').child(auth.uid).child('productCounter').val() < 1
如果该节点中存在任何数据(以允许更新节点)
data.exists()
如果请求 uid
节点有 isBusiness
到 true
root.child('YOUR_PATHS').child('users').child(auth.uid).child('isBusiness').val()
当前的实现存在竞争条件:'non-business' 用户可以创建任意数量的产品,直到将第一个 productId 写入用户文档。
在我看来,解决此问题最直接的方法是为非业务用户预填充产品 ID,并限制他们分配新产品的能力。
- 创建非企业用户时,您 create/reserve 为他们提供一个唯一的 productId,他们可能会使用也可能不会使用。存储在用户文档中。
- 当非业务用户创建产品时,他们只能将数据写入该产品 ID。虽然不需要修复所描述的用例,但根据您的安全要求,您可能希望通过安全规则或云功能强制执行此操作。
- 无论多少个并发选项卡尝试创建产品,它们都将始终写入相同的 productId。
public createProduct(id: string, product: Product, user: User) {
if (user.isBusiness) {
return this.afStore.collection('products').doc(id).set(product, { merge: true });
} else {
return this.afStore.collection('products').doc(user.reservedProductId).set(product, { merge: true });
}
}
警告:与 any 前端代码一样,这不会阻止恶意行为者随意操纵它。如果此行为是一个安全问题,您将希望限制非业务用户对产品集合的写访问。考虑实施适当的安全规则/云功能。你只能信任后端。
我有一个 Angular web-app,使用 Firebase 作为无服务器后端技术。 web-app 当前的目的是让注册用户为提供的产品创建文档。虽然企业用户可以创建无限 product-docs,但免费用户应该只能创建一个 product-doc。
Collections:
- 用户 -
{id: string, isBusiness: boolean, name: string, email: string, productIds: string[]}
- 产品 -
{id: string, title: string, description: string, userId: string}
每当用户使用
创建新的 product-doc public createProduct(id: string, product: Product) {
return this.afStore.collection('products').doc(id).set(product, { merge: true });
}
我运行一个带有触发器onCreate
的云函数将创建的product-doc的id
添加到相应[的productIds
字段=45=]。这允许我检查 frontend-side if(!user.isBusiness && productIds.length > 1)
以限制为免费用户帐户创建多个 product-docs。
然而,我最近做了一个 pen-test 并且测试人员偶然发现,如果您在多个浏览器选项卡上打开网站并同时 运行 每个选项卡上的 createProduct()
功能同时,所有这些都将执行,允许非业务用户拥有多个 product-docs.
有没有人处理过类似的情况?我不知道是否有办法通过 Firestore 安全规则避免这种情况,或者是否需要等待活动事务完成。
在用户中创建一个 productCounter
节点。然后将您的规则与以下内容合并:
{
"rules": {
"cascade_your_nodes": {
"products": {
"$productId" : {
.".write": "root.child('YOUR_PATHS').child('users').child(auth.uid).child('productCounter').val() < 1 || data.exists() || root.child('YOUR_PATHS').child('users').child(auth.uid).child('isBusiness').val()"
}
}
}
}
}
推理:
如果它不与任何现有规则冲突,则仅在以下情况下才允许写入任何 productId
:
请求
uid
节点有productCounter
小于1root.child('YOUR_PATHS').child('users').child(auth.uid).child('productCounter').val() < 1
如果该节点中存在任何数据(以允许更新节点)
data.exists()
如果请求
uid
节点有isBusiness
到true
root.child('YOUR_PATHS').child('users').child(auth.uid).child('isBusiness').val()
当前的实现存在竞争条件:'non-business' 用户可以创建任意数量的产品,直到将第一个 productId 写入用户文档。
在我看来,解决此问题最直接的方法是为非业务用户预填充产品 ID,并限制他们分配新产品的能力。
- 创建非企业用户时,您 create/reserve 为他们提供一个唯一的 productId,他们可能会使用也可能不会使用。存储在用户文档中。
- 当非业务用户创建产品时,他们只能将数据写入该产品 ID。虽然不需要修复所描述的用例,但根据您的安全要求,您可能希望通过安全规则或云功能强制执行此操作。
- 无论多少个并发选项卡尝试创建产品,它们都将始终写入相同的 productId。
public createProduct(id: string, product: Product, user: User) {
if (user.isBusiness) {
return this.afStore.collection('products').doc(id).set(product, { merge: true });
} else {
return this.afStore.collection('products').doc(user.reservedProductId).set(product, { merge: true });
}
}
警告:与 any 前端代码一样,这不会阻止恶意行为者随意操纵它。如果此行为是一个安全问题,您将希望限制非业务用户对产品集合的写访问。考虑实施适当的安全规则/云功能。你只能信任后端。