Back

Securing files/images.


Nertskull posted 2 years ago

I'm new to Laravel. In previous projects I have had files/images that I want to be accessed ONLY by those who have logged in to my website.

To do that, I often would put the files outside of the document root, then use PHP to retrieve the files relative to the server root (not website root) and send the files on to the website being visited by an authenticated user.

Is there a better way in Laravel? I'm thinking that perhaps now I can just put the files within the site root, and then rely on middleware to prevent others from accessing the files???

If someone has the direct path to the file, can they still download it? Or will the routing system prevent someone from accessing that directory without first passing it through the laravel stuff?

What's the best location for files I want to be secured from non-registered users using laravel?

I've read something about using Response::download, is that preferred for this type of usage?

If it is, then I'm not sure how to get Response::download to send a file to <img src="....path here..." />

(I should also maybe say I'm using nginx, so htaccess is not the way, although I could write nginx rules if needed, but I'd prefer not to)

Xum replied 2 years ago Solution

If you put a file in public folder it will be accessible to everyone who knows the file name, because nginx/apache rewrite rules used by Laravel only apply to non-existing files, so Laravel won't even be run when accessing an existing file.

So, you still have to put restricted files somewhere out of public folder. Maybe in storage folder, but ultimately it doesn't matter.

And yes, you should just use Response::download.

Make a small FileController:

class FileController extends Controller {
	public function __construct()
	{
		$this->middleware('auth');
	}

	public function getFile($filename)
	{
		return response()->download(storage_path($filename), null, [], null);
	}
}

The fourth argument of download() being null prevents the Content-Disposition header being set to attachment. So your browser won't ask you save the file, but just show it.

Then add a route:

Route::get('file/{filename}', '[email protected]')->where('filename', '^[^/]+$');

And that's it. Now, your authenticated users can download files from storage folder (but not its subfolders) by calling http://yoursite.com/file/secret.jpg. Add you can use this URL in src attribute of an image tag.

Nertskull replied 2 years ago

Xum said:

If you put a file in public folder it will be accessible to everyone who knows the file name, because nginx/apache rewrite rules used by Laravel only apply to non-existing files, so Laravel won't even be run when accessing an existing file.

So, you still have to put restricted files somewhere out of public folder. Maybe in storage folder, but ultimately it doesn't matter.

And yes, you should just use Response::download.

Make a small FileController:

class FileController extends Controller {
  public function __construct()
  {
  	$this->middleware('auth');
  }

  public function getFile($filename)
  {
  	return response()->download(storage_path($filename), null, [], null);
  }
}

The fourth argument of download() being null prevents the Content-Disposition header being set to attachment. So your browser won't ask you save the file, but just show it.

Then add a route:

Route::get('file/{filename}', '[email protected]')->where('filename', '^[^/]+$');

And that's it. Now, your authenticated users can download files from storage folder (but not its subfolders) by calling http://yoursite.com/file/secret.jpg. Add you can use this URL in src attribute of an image tag.

Perfect, this seems to work just great. Also I didn't know about the 4th argument, so that makes it even better. I set it to 'inline' and I get it to be viewed in the browser.

Things are working great now.

Thanks!

benmclendon replied 7 months ago

Great Solution!

I needed to be able to specify a folder so...

Route:

Route::get('file/{foldername}/{filename}', '[email protected]')->where('filename', '^[^/]+$');

Controller:

public function getFile($foldername,$filename)
{
    $fullpath="{$foldername}/{$filename}";
    return response()->download(storage_path($fullpath), null, [], null);
}
warrio4 replied 2 months ago

Great question and answer! if you're looking for a file within the subfolder (i.e. personal) like http://yoursite.com/file/personal/secret.jpg the root above won't work. Change the where to:

->where('filename', '^(.+)\/([^\/]+)$')

Sign in to participate in this thread!



We'd like to thank these amazing companies for supporting us