8
votes

I don't understand how can I set a limit of files upload in a day. I want users to publish a maximum of 10 photos per day. On the database side I put a increment counter. If it reaches a certain size it does not allow a user to post other contents. but by the storage side this is impossible. An attacker can publish all files that he want without limits. Is there a solution to prevent this situation? Thanks in advance. At moment my security rules are:

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
There are a couple of things you can do. 1) Implement rules that only allow validated users to read/write to the node in question. That would prevent an attacker for just uploading to any node. 2) Implement a 'upload_count' node for the day and have uploads (writes) include the upload as well as a timestamp. Set up a validation rule which will only allow uploading if the count is less than 10 uploads for that day. You'll want to look at the predefined rule variables 'now' and 'new data' and compare the now to the timestamp of the data attempting to be written.Jay
if you see my rules, each user can write only in its node, but the problem is that each user can write unlimited files in its node. i not understand the second point. in the database i have already a counter but in the storage how can i store a counter? Remember that the database and storage rules are different and separate and an attacker can upload a file without update the counter in the databaseDThink
no way to solve this?DThink

1 Answers

4
votes

Well, there's a very simple way of doing this, and there's the right way of doing this.

The hacky way of only allowing a certain number of files to be uploaded in a certain time period is to name the files with some numerical property: say users/{userid}/0.jpg through users/{userid}/9.jpg (for 10 photos).

You can write a rule to check that as follows:

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

If you need more granularity than order of magnitude, you can do something like:

// 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
}

That only solves half our problem though: we can restrict the number of files, but what if a user just wants to upload over them? Luckily, we can write a rule that prevents an end user from overwriting their file ever (though we've got to carve out deletions), or within a given time period. Let's explore:

// 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
}

These can be combined into function:

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()
}

Long term, the solution is being able to reference Database data from within Storage, and vice-versa. Unfortunately, that world isn't here yet, but we're working towards it.