How to protect images from public view in Laravel
If you are running a image website, where user upload images and unauthenticate users cann't access it. Or your website are collecting images or any files which should be accessible to authenticated users only. In this situation, file protection is important part of coding.
In this article, I will share you how you can protect files from accessing to public view. We will make that only authenticate user access its profile picture.
Laravel's file configuration file is located at config/filesystems.php. The default local driver set to storage/app folder.
You need to create symbolic link of storage folder to public folder. For that run the below storage:link artisan command.
php artisan storage:link
The profile picture view route is protected with auth middleware. Set the route in routes/web.php file.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProfileController;
Route::get('images/profile/{user_id}/{slug}', [ProfileController::class, 'showProfilePicture')->name('profile.show')->middleware('auth');
In your controller, check with user id or the folder name which is unique with user field like username. This will used to check with Auth::user().
<?php
namespace App\Http\Controllers;
use Auth;
use Illuminate\Http\Request;
class ProfileController extends Controller
{
/**
* show user profile picture
*
* @return void
*/
public function showProfilePicture($user_id, $filename)
{
$profile_path = storage_path('app/public/images/profile/' . $user_id . '/' . $filename);
if (Auth::user()->id == $user_id) {
return response()->file($profile_path);
} else {
abort(404);
}
}
}
This way, you can serve other images or files to users. If any user with other id or username will try to access in browser, it will return 404 error page.
Now in the second scenario, if you don't want user to access direct from entering image path, then you can use session token.
In this way, you need to create a random string on previous page that link to the image link. Also set the string in session, so when user access the image, image route will check the string with session.
To do this, add two routes, one for the page which will view link for image or any file and second will show page.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProfileController;
Route::get('profile/', [ProfileController::class, 'profile'])->name('profile')->middleware('auth');
Route::get('images/{filename}', [ProfileController::class, 'showProfilePicture'])->name('profile.show')->middleware('auth');
In the controller file, add the two controller methods. If you want user to access image only once, you can set session to forget when user access it otherwise user can access the image until the session expires.
<?php
namespace App\Http\Controllers;
use Session;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
class ProfileController extends Controller
{
/**
* profile view
*
* @return void
*/
public function profile()
{
$img_token = Str::random(48);
Session::put('img_token', $img_token);
return view('profile', compact('img_token'));
}
/**
* show user profile picture
*
* @return void
*/
public function showProfilePicture(Request $request, $imagename)
{
$profile_path = storage_path('app/public/images/'.$imagename);
$img_token = Session::get('img_token');
if ($img_token == $request->img_token) {
Session::forget('img_token');
return response()->file($profile_path);
} else {
abort(404);
}
}
}
In the resource path of profile view, set image link as below. Session string will pass in the query string which will check when user access the image.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Profile page</title>
</head>
<body>
<a target="_blank" href="{{ route('profile.show', 'anyfile.jpg') }}?img_token={{ $img_token }}">Profile</a>
</body>
</html>
This way, you can unauthorize access to user direct accessing image or any file.
Copyright 2023 HackTheStuff