Search code examples
phplaravelserverfile-permissionsuser-permissions

How can I securely store user folders in a server?


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.


Solution

  • You have to make a distinction between the public and private files.

    Public 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.

    Private files

    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.