Use Laravel storage folder:
https://laravel.com/docs/5.3/filesystem.
For example:
$attachments = $request->file('attachment');
$attachment->storeAs($path, $uniqueFileName);
I am trying to create a file sharing application, where authenticated users upload and download files. But even though users are not logged in they can access to my uploads folder which is inside the public folder and download the files. So my question is how to make access to the files to only the authenticated users. I am using Laravel 5.3.
Use Laravel storage folder:
https://laravel.com/docs/5.3/filesystem.
For example:
$attachments = $request->file('attachment');
$attachment->storeAs($path, $uniqueFileName);
Anything inside the public folder is accessable to the wide world (it's your "public_html" folder so-to-speak).
You would either have to do what lars6 mentioned and use Laravel's storage stuff (which stores it ouside of the public folder and in it's storage folder), or write your code so that it goes a level above the public folder, or use a .htaccess file to restrict access.
out of the three, Laravel's system is probably the best bet.
I have a solution that uses public folder. When you upload files you can save them in whatever folder you want, you then save that folder in database together with filename. Then you generate a hash as well and use that in your route to access a particular file or you can use filename instead of hash, up to you. When you check the hash and find the coresponding file in the database you then serve that file from public folder directly from router.
Here is an example for images:
$image = Cache::rememberForever('image_'.$hash, function() use($hash){
return Image::where('hash', $hash)->first();
});
if($image == null)
abort(404, 'The image you are looking for could not be found.');
$imagefile = file_get_contents($image->path.'/'.$image->hash . '.' . $image->extension);
header('Content-type: image/jpeg;');
header("Content-Length: " . strlen($imagefile));
echo $imagefile;
user can't access any files in the public folder since they do not know where in what folder you save the files and since you render the file directly from controller function they will think if they type
www.yourwebsite.com/files/Yq4VgtC.jpg
that jpg image they just tried to access is actually stored directly under files folder when neither files folder exists nor that image directly under it.
Image could be somewhere like this
public/uploads/2016/12/28/15
so year/month/day/hour
etc that's just an example, you can store it where you want and noone will figure it out.
and before they can access any files you can use middleware to check if they are logged in if they try to access
www.website.com/files route
here is how a route can look for example image in my case:
Route::get('{hash}.{extension}', 'ImageController@image')->where('hash', '[a-zA-Z0-9-]+')->where('extension', '[jpg, jpeg, gif, png, mp4, bmp]+');
The issue with that is that the files are still exposed publicly as they are in the public area/folder. Yea it would be a lot harder to find (as your not giving away it's exact name or location), but still possible.
For access control use a simple middleware. And for the output a simple php stream buffer.
@xiller I used the Laravel Filesystem to create a simple microservice to store assets in Amazon S3 buckets, public or private.
it's pretty much impossible to find the files using my system, I'd love you to prove me wrong :) You can block the folder listing in .htaccess so noone would be able to browse the folders by guessing folder names and you have no chance of finding a file if you do not know what kind of folder structure programmer used to store the files.
for example you could randomize the upload folder so that not even you know where it's uploaded
'public/uploads/2016/12/06/15/' . str_random(7) .'/filename.mp4';
No one no matter what software they used to scan your website would figure it out, and you could ofcourse block anyone using robots to probe your folder structure to find out files. No normal users would do this so it's pretty much safest way to do it if you indeed decide that you must use public folder to store files that should only be visible to certain users.
Why not simply put your files outside of public? Laravel/PHP can still complete the file_get_contents() method, while making 100% percent sure your files are not directly accessible through regular unauthenticated HTTP requests. Don't let anything up to luck, better safe than sorry :)
you are right about that, I am still learning and back then 9 months ago when I was implementing that I didn't think of that idea
Alright no problem! Wanted to know just in case you had a point against it.
Also wanted to make sur it was written somewhere in the thread in case somebody would stumble on it like I just did.
Have a nice day!
@maxnb it's not impossible at all to find the files using your system. It all depends on the purpose.
Because if I've downloaded the files once, the location is saved in my history. Now, say I cancelled my membership at your site, I shouldn't be able to access the files any more. After all, im not paying for them any more. Yet, using the direct link in my browser's history, I just have visit the link and I'm there.
Even worse, I could pass that URL on to friends and they could download it, even if they don't even have an account.
@Thyrosis wrong
you could not pass anything to anyone, nor could you download anything if not logged in. My system is bullet proff, but I agree with macparent that storing outside of public folder adds another layer of protection and is best practice, but my system as it is no-one on the planet could break it, it's just impossible to download anything when you are going through the controller to get the files.
$imagefile = file_get_contents($image->path.'/'.$image->hash . '.' . $image->extension);
and
Route::get('{hash}.{extension}', 'ImageController@image')->where('hash', '[a-zA-Z0-9-]+')->where('extension', '[jpg, jpeg, gif, png, mp4, bmp]+');
you as the visitor see this www.imgur.com/filename.jpg
there is no file there, that is a route that points to the controller that gets the file for you, and it can also check if you are logged in and have right level of access to a file. You can send that link to your friends, but they would see nothing
@maxnb is it like hashing password in authentication process?
Considering the above (and many other posts about private files), I want to create a system where my authenticated users, who can create PDFs of reports, can only see their own reports and not those of other users. I can't rely on simple authentication-level access, as an authenticated user must not be able to see another authenticated user's files. Every document and post refers to simply privacy where if you're authenticated and have the correct role you can see private files - no differentiation for user file ownership.
Is there a "Laravel way" to do this, or should I be storing private files in directories named for user ID's and manually checking logged-in user ID against the directory they're trying to access to block visibility to only their own files?
Before I go off building this I'm trying to see if there's a better/ready way to do this. I'm afraid that if I build my own it will catch me out later when I migrate my app to a host because I inadvertently hard-coded something that won't work in a different environment. I'd prefer to keep within accepted Laravel practices and ecosystem.
I guess the obvious solution is to not use the public driver and store your files within a publicly accessible folder. Using random file names is also a bit of a naff solution.
Store the files in a private file system (i.e. AWS S3). When the user requests a file via a unqiue url i.e. /downloads/[file-hash-here] direct them to a controller;. Here you can verify all access to the file using middleware for example.
This method would generate urls like /downloads/w8q379trcb98wq3r. Alternatively, you could still use the file id within the url i.e. /downloads/123 if verifying ownership before downloading.
I want to create a system where my authenticated users, who can create PDFs of reports, can only see their own reports and not those of other users
@Hondaman900 It's best to start a new topic, but yes, you'd do that with authorization in addition to authentication: https://laravel.com/docs/5.6/authorization
Use storage on a non-public disk. The controller retrieves the file and sends it to the browser, after passing authorization or denying it if they are not. https://laravel.com/docs/5.6/filesystem#retrieving-files
Then no one can download it, even with a direct link (because it goes through the controller/policy) unless they pass your authorization policy (like, the person trying to download it is the person who created it, etc)
@NIKOCRAFT - if file exists inside the folders?
Please or to participate in this conversation.