Reputation: 75
I'm working on a project using PHP (Laravel Framework). This project allows logged-in users to store files and folders that only they can access.
I've set up user authentication such that a unique user token is required to access the user's data. When a user stores/creates a folder, the folder gets stored on the server, prepended with a random 32 character string complicated path such as /WijiIHSIUhoij99U8EJIOJI09DJOIJob/username/folder which is stored in a MySQL database for the user to fetch with an API so long as they have the correct access token for their account. I did this because I guess it seemed more secure, correct me if I'm wrong.
This is half the job (please correct me if what I've done so far is wrong or bad practice). What I need now is to make sure nobody can access the root folder / and get a list of user folders. But I fear if I restrict access to the root folder, users will no longer be able to access their folders.
I don't know where to even start looking to find a solution, so anything, even if it's just to get me started will help.
Upvotes: 0
Views: 706
Reputation: 3847
You have to make a distinction between the public and private files.
Public files can be accessed, you guessed it, by everyone. They are images, logos, documentations... This kind of files. If you have the url of these files, you can download them.
Things are a little more complicated for private files. These files should not, in any case, be downloadable without authentification. Said otherwise, only the user that own these files (and often the admins too) should be able to download them. For instance, these files could be an invoice for your order, a paid media...
For the directory structure, you are free, it's up to you. The only important thing is to prevent these files from being publicly accessible, so you MUST NOT put them somewhere in the public
folder (and the linked storage/app/public
).
By default, Laravel expects these files to be in storage/app
. For instance, it could be storage/app/users/1451
for the user with id 1451. It's not important to randomize thes paths since these files are not accessible directly (they are NOT in the public path).
So you may ask, how a user can download them? The answer is to create an authentificated route and check for user authorization before sending the file back in the response.
You can do something like:
Route::get('invoices/{invoice}/download', [InvoiceController::class, 'download']);
Then, you apply a policy to this route, where you check that the authenticated user is able to download the file (like you would do for any other authenticated routes).
If he can, well, perfect, just return the file in the response: return response()->download($path_to_the_file);
. If he can't, he will get a 403 Forbidden
.
Upvotes: 1
Reputation: 581
The random string is redundant, as your token does the randomization for you. If the users know that their stuff is stored in /RANDOMSTRING/folder, someone will build a bot to try them all anyhow, but the idea is close to what you want, I think.
Instead, try creating a script that catches all 404s, and checks if they match RANDOM_STRING/user/folder. If so, then you can have the random string in the database match to a universal folder, something like /user_files/username/their_folder. This way, you aren't exposing your folder structure, and you can add a middleware to count how many times they've unsuccessfully tried a URL, then ban their client if it's more than 3, for instance.
In this case, the script delivers the files (or the folder) as a download, or as a screen without the actual folder structure visible. The files can be stored anywhere, and the database correlates the URL entered with an actual file or folder somewhere. (really, we're trying to mitigate someone getting access through a brute force here).
From there, I would look into using a different service to store the files. This helps with two things that you can read up on. First, you can choose something like AWS cold storage which will cost you less, and second you can take the storage off the computational server, such that if there is a security breach, you know that they either only had access to the user files, or the code files. This will help you in the long run (although if they gain access to your code files, they will likely be able to locate your storage location and password... this is really to protect your code files).
Beyond that, start looking into read and write permissions and user groups. PHP, the OS, the users, and the admins should all have their own permissions on the storage server to make sure they can't do certain things with the files. Moreover, you don't want someone uploading a malicious file and having it execute.
This is just a primer, but it'll lead you to a LOT of information, I'm sure.
Upvotes: 4