Laravel: How to prevent users loading files from outside the base path - laravel

I have a Laravel website and I have several routes that load the contents of images from Storage. I do this using the following code:
public function show_image($name) {
echo Storage::disk('images')->get($name);
}
I want to prevent users being able to set name to something like ../../../error.log. So I don't want users to escape the Storage directory. I have a few ideas on how to accomplish this however I want to know is there a best practice?

If you need just file name, not location, disallow them from inputting folder of any kind. Just cut the string on /.
end(preg_split("#/#", $name));
When you need to allow some folders and all of the contents, check the folder name, subfolder name, etc.

You could either keep a registry/index of the uploaded images, and only allow the user to show a image from that registry (e.g. an images database table).
Or you could do a scan of the directory, that you are allowing files from, and make sure, that the requested file is in that list.
public function show_image($name) {
$files = Storage::disk('images')->files();
if (! in_array($name, $files)) {
throw new \Exception('Requested file not found');
}
echo Storage::disk('images')->get($name);
}
(code untested)

Related

Get correct URL for uploaded files

I have a function in my platform for letting users upload their own icon images. Once they've uploaded them I save them using $request->icon->store('public/icons') and simply save the returned path, something like "public/icons/xa6y2am3e4cOdqQuLpvEhFSXDKwFMDOgggS2i67l.png".
I'm not really clear though on what's the correct way to show the icons. The URL for showing the icon in the above example is "/storage/icons/xa6y2am3e4cOdqQuLpvEhFSXDKwFMDOgggS2i67l.png", thus I need to replace "public" in the path with "storage", which makes me think I've done something wrong somewhere (I see no reason why the store() method should provide me with a path that's not correct).
I have done my symlinks as described in the documentation. Using the local storage. What am I missing?
This is how I handle my storage in one of my apps that includes blogs:
$storedImageName = $request->file('main_image')->store('blog_images', 'public');
The store method actually returns the stored file name along with the path. Here, I specify that my disk is public and that the folder inside public is blog_images. I store the result of that in $storedImageName.
Now, to save it in the database, I do not actually save the whole path, I save ONLY the image name. This is because, you may want to update your directory name in the future, or move it to another location, etc. You can get that like this:
$onlyImageName = basename($storedImageName);
basename() is PHP's function, has nothing to do with Laravel.
This way, I can render my images in my blade files like this:
<img ... src="{{ asset('/storage/blog_images/' . $blog->main_image) }}" />
asset() helper will provide you with path to your public directory, and then you just need to specify the rest of the path to your image.

Remove "/public" from file path in Laravel when linking public directory

I use Laravel of 5.7.25 version.
I have a symbolic link from public/storage to /storage/app/public.
I'm storing files in /storage/app/public/place-images directory.
I keep the path of stored file in table files which keeps all stored files. The file path would be public/images/some_hash.jpg.
Now I made a file resource, which is used when I'm getting files from my API. The file path retured from api equals public/images/some_hash.jpg. But instead I need it to be images/some_hash.jpg. However, in the table I prefer to keep real path of the file related to the storage folder. After all, I can keep files in AWS or somewhere else.
As far as I understand storage is the root of my disk. The $file->store() method includes public part of the file path.
I end up doing something like this:
// This is ImageResource file, Image model has File relation. One Image has one File
// TODO: Dirty hack
$path = $this->file->path; // This equals 'public/place-images/hash.jpg'
// Removing 'public' part
$charIndex = strpos($path, '/');
$path = substr($path, $charIndex + 1);
return [
'id' => $this->id,
'original_name' => $this->file->original_name,
url' => asset('storage/' . $path) // now we have /storage/place-images/some_hash.jpg which is correct
];
Is there anyway to avoid this dirty hack? I think I'm missing something obvious
storage is not the root of your disk. You can set the root for a disk in config\filesystems.php.
Default root for the public disk defined in config/filesystems.php is /storage/app/public so just store place-images/hash.jpg without public.
If you want to keep files from different disks in one table just add a field with a disk name to this table.

Place directly into folder without adding UUID folder

I am sorry if this has been answered before but all my searching is not coming up with a result.
I would like to place files directly into the target path and it not generate the UUID folder and then place the file in there. I know about the whole same filename could exist that is why I change the filename on the onChange event before uploading
I have tried to modify the handler.php but either I am not editing the correct lines or something else is going on.
After long and tiring, trying to figure this out, hours I have found a work around on this.
If you sent a blank uuid to the script it will not create the folder and will just place the file in the folder that you told the endpoint to put items. Not sure if this is how the script is supposed to work but it works for me.
I do not have to worry about files that are named the same as i have the script also change the file name before it gets upload with pre-prending a unique string to the file name.
callbacks:
{
onSubmit: function(id, name) {
this.setUuid(id, "")
console.log("onSubmit called")
}
}

Laravel 5 Filesystem - Directory Permissions

I'm writing a Filesystem with a Login System at the moment in Laravel 5 and at the moment every User can create folders and upload files but, when I copy the path from the folder "A" from User "1" and log out and login with User "2" and paste the path in the Browser , I have acess to folder "A" from User "1".
I want that only the person which create the folder can open it and Upload files. I want a authentication via a User ID that checks if the currently user who is trying to open the folder/path is the creator and have the permission to do that.
public function mkdir(Request $request) {
$validator = Validator::make ( $request->all (), [
'dirname' => 'required|max:20'
] );
if ($validator->fails ( $request )) {
return redirect ( 'filesystem' )->withErrors ( $validator )->withInput ();
} else {
$id = Auth::user ()->id;
$dir = $request->dir . "/" . $request->dirname;
Trayer:
Because folder structure and ownership of the folder is set to users on the server, doing something like this without some sort of database is not possible. What I would suggest is something like what noodles_ftw suggests.
Create a table folderPermissions and in this folder, it would have a couple columns. owner, path
Owner would be the id of the user that created the folder, and path would be the path to the folder.
Next you would create some sort of middleware or other check to say baiscally the following
// get current user id
// get folders owned by that user
// if user is not allowed to view folder
// return an error letting them know
// continue to do what you need to do with the folder
This will allow you to run your checks and even alert that a user tried to access a folder that they were not allowed to access.
One of the other options you have here is to use the servers .htaccess and .htpasswd files. This would allow you to set up a list of users allowed to get into the folder. you would want to create these files when a user creates new folders. Use the users password and username from the database when generating the .htpasswd file for the user.
Find more information about this solution from the documentation

How to use storage_path() to view an image in laravel 4

I'm using storage_path() for storing uploaded images, but when I use it is pointing wrong on my page.
I use it like this {{ $data->thumbnail }} where $data came from the database and thumbnail comes as the string which used storage_path
Let us take a look at the default L4 application structure:
app // contains restricted server-side application data
app/storage // a writeable directory used by L4 and custom functions to store data ( i.e. log files, ... )
public // this directory is accessible for clients
If I were you, I would upload the file to the public directory directly:
Store image here: public_path() . 'img/filename.jpg'
Save the 'img/filename.jpg' in database
Generate the image URL with url('img/filename.jpg') => http://www.your-domain.com/img/filename.jpg
Hope this helps.
The storage_path function returns the path to the storage folder, which is inside the app folder --and outside the public folder-- so it's not directly accessible from the client, that's why your images are not being displayed. You can move them to the public folder path, or you could use a custom controller to handle the image requests, read the image from the storage folder and return the value.

Resources