【问题标题】:firebase storage-prevent spamfirebase 存储 - 防止垃圾邮件
【发布时间】:2017-02-01 12:41:08
【问题描述】:

我不明白如何设置一天内上传文件的限制。我希望用户每天最多发布 10 张照片。在数据库方面,我放置了一个增量计数器。如果它达到一定大小,则不允许用户发布其他内容。但在存储方面这是不可能的。攻击者可以无限制地发布他想要的所有文件。有没有办法防止这种情况发生?提前致谢。目前我的安全规则是:

service firebase.storage {
  match /b/projectid/o {
    match /Photo/{user}/{photo}/image.jpg {
      allow write: if request.auth != null && 
                      request.auth.uid == user && (
                      request.resource.size < 5 * 1024 * 1024 && photo.size() < 32 || 
                      request.resource == null);
      allow read: if request.auth != null && 
                     request.auth.uid == user
    }
  }
}

【问题讨论】:

  • 您可以做几件事。 1) 实施仅允许经过验证的用户读取/写入相关节点的规则。这将阻止攻击者仅上传到任何节点。 2) 实现当天的“upload_count”节点,并让上传(写入)包括上传和时间戳。设置一个验证规则,仅当当天的上传次数少于 10 次时才允许上传。您需要查看预定义的规则变量“now”和“new data”,并将 now 与尝试写入的数据的时间戳进行比较。
  • 如果你看到我的规则,每个用户只能在其节点中写入,但问题是每个用户可以在其节点中写入无限的文件。我不明白第二点。在数据库中我已经有一个计数器但是在存储中我如何存储一个计数器?请记住,数据库和存储规则是不同且独立的,攻击者可以在不更新数据库中的计数器的情况下上传文件
  • 没有办法解决这个问题?

标签: firebase firebase-security firebase-storage


【解决方案1】:

嗯,有一种非常简单的方法可以做到这一点,而且有正确的方法。

只允许在特定时间段内上传特定数量的文件的hacky方法是使用一些数字属性命名文件:比如users/{userid}/0.jpgusers/{userid}/9.jpg(10张照片)。

您可以编写一个规则来检查,如下所示:

// Match all filenames like 0.jpg
match /users/{userId}/{photoId} {
  allow write: if photoId.matches('^\d\.jpg$')
}

如果您需要比数量级更多的粒度,您可以执行以下操作:

// Match all filenames like YYY.jpg where YYY is a number less than XXX
match /users/{userId}/{photoId} {
  allow write: if int(photoId.split('\.')[0]) < XXX
}

这只能解决我们一半的问题:我们可以限制文件的数量,但如果用户只想上传文件怎么办?幸运的是,我们可以编写一个规则来防止最终用户覆盖他们的文件(尽管我们必须排除删除),或者在给定的时间段内。让我们探索:

// Allow files to be overwritten once a day, written if there's nothing there, or deleted as often as desired
match /users/{userId}/{photoId} {
  allow write: if request.time > resource.timeCreated + duration.value(1, "d") || resource.size == 0 || request.resource.size == 0
}

这些可以组合成函数:

function isAllowedPhotoId(photoId) {
  return int(photoId.split('\.')[0]) < XXX
}

function canOverwritePhoto() {
  return request.time > resource.timeCreated + duration.value(1, "d") || resource.size == 0 || request.resource.size == 0
}

match /users/{userId}/{photoId} {
  allow write: if isAllowedPhotoId(photoId) && canOverwritePhoto()
}

从长远来看,该解决方案能够从存储中引用数据库数据,反之亦然。不幸的是,这个世界还没有到来,但我们正在努力实现它。

【讨论】:

  • 我发现,当我删除文件时,使用这种方法会出现问题。如果我删除某些文件,我有一个包含非有序文件的文件夹。当我想保存具有特定 ID 的照片时,这有点挑剔
  • 你是对的——它给客户端带来了正确重命名文件的负担。您可以选择一个简单的散列方案:通过 n 修改文件名,然后将其放在该位置。如果失败,则增加 1(线性探测)或 n^2(二次探测)并将文件添加到那里。
  • 好的。我希望将来在存储和数据库之间建立一个真正统一的平台。例如,我记得当我使用 Parse 时,如果我上传一张图片,我会自动将这张图片的引用保存在我的数据库中。我认为类似的功能可能很有用。谢谢,干得好
  • 同意这一点——我们正在努力。也就是说,通过拆分,我们获得了许多优势(更强大的上传和下载、高达 5TB [vs 10MB] 的文件大小、对文件的原始访问、降低成本、控制对象生命周期以及很快能够选择存储文件的区域)。
  • 好的,我尝试使用线性方案。问题是,在任何上传失败的情况下,我都会使用带宽并且照片> 1MB,该过程非常慢,因为在任何上传失败时,客户端都会尝试在下一个位置重新上传。不是一个好的解决方案
猜你喜欢
  • 1970-01-01
  • 2011-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多