DEV Community

Cover image for Laravel Security Guide: 6 Common Mistakes and How to Fix Them
Asaba William
Asaba William

Posted on

Laravel Security Guide: 6 Common Mistakes and How to Fix Them

Laravel is one of the most popular PHP frameworks, but popularity attracts attention — including from attackers.

Even experienced developers can overlook security pitfalls that leave their applications vulnerable.

This guide covers six common Laravel security mistakes (based on the Laravel Security Guide video) and provides practical fixes with examples you can use today.

1. Invalidate Sessions After Password Changes

The mistake:

When a user changes their password, existing sessions on other devices often remain active. This means if an attacker already has access, they stay logged in even after a password update.

The fix:

Use Laravel’s logoutOtherDevices() method and auth.session middleware to invalidate all other active sessions.

Example:

// In your password update logic
use Illuminate\Support\Facades\Auth;

public function updatePassword(Request $request)
{
    $request->validate([
        'password' => 'required|confirmed|min:8'
    ]);

    $user = Auth::user();
    $user->password = bcrypt($request->password);
    $user->save();

    // Invalidate other sessions
    Auth::logoutOtherDevices($request->password);

    return back()->with('status', 'Password updated and other sessions logged out!');
}

Enter fullscreen mode Exit fullscreen mode

Why it matters:

If an attacker had stolen a session token, they’ll be kicked out immediately after the user updates their password.

2. Secure File Downloads

The mistake:

Directly linking to file paths (e.g., /storage/invoices/invoice1.pdf) allows unauthorized users to guess URLs and access private files.

The fix:

Use temporary signed URLs that expire after a set time, and validate signatures.

Example:

// Generating a signed URL
use Illuminate\Support\Facades\URL;

Route::get('/download/{file}', [DownloadController::class, 'download'])
     ->name('download')
     ->middleware('signed');

// In a controller
public function getSignedDownloadLink($file)
{
    return URL::temporarySignedRoute(
        'download',
        now()->addMinutes(10),
        ['file' => $file]
    );
}

// Validate in the route
public function download(Request $request, $file)
{
    if (! $request->hasValidSignature()) {
        abort(403, 'Invalid or expired link');
    }

    return response()->download(storage_path("app/private/{$file}"));
}

Enter fullscreen mode Exit fullscreen mode

Why it matters:

Even if someone finds the link, it won’t work after the expiration time.

3. Scope Route Model Binding

The mistake:

Without scoping, a logged-in user can access another user’s resource just by guessing the ID.

The fix:

Use Laravel’s Scoped Bindings to ensure resources belong to the authenticated user.

Example:

// routes/web.php
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
    return $post;
})->scopeBindings();

// This ensures $post belongs to $user

Enter fullscreen mode Exit fullscreen mode

Why it matters:

This prevents horizontal privilege escalation, where users try to access others’ data.

4. Generate Unique Filenames

The mistake:

Saving files with their original names can lead to overwrites or information leaks.

The fix:

Generate unique names, for example using UUIDs.

Example:

use Illuminate\Support\Str;

public function upload(Request $request)
{
    $file = $request->file('avatar');
    $uniqueName = Str::uuid() . '.' . $file->getClientOriginalExtension();
    $file->storeAs('avatars', $uniqueName, 'public');

    return back()->with('status', 'File uploaded!');
}

Enter fullscreen mode Exit fullscreen mode

Why it matters:

Even if two users upload profile.jpg, they won’t overwrite each other’s files.

5. Encrypt IDs in URLs

The mistake:

Exposing raw IDs in URLs makes enumeration attacks easy — attackers can just increment numbers.

The fix:

Encrypt IDs before sending them in URLs, and decrypt when receiving.

Example:

use Illuminate\Support\Facades\Crypt;

// Generating an encrypted link
$id = 123;
$link = route('profile', ['id' => Crypt::encryptString($id)]);

// Receiving the encrypted ID
public function show($encryptedId)
{
    $id = Crypt::decryptString($encryptedId);
    $user = User::findOrFail($id);

    return view('profile', compact('user'));
}

Enter fullscreen mode Exit fullscreen mode

Why it matters:

Attackers can’t guess sequential IDs if they’re encrypted.

6. Encrypt Sensitive Database Fields

The mistake:

Storing sensitive data like API keys or personal identifiers in plaintext makes breaches devastating.

The fix:

Use Laravel’s Custom Casts to encrypt/decrypt automatically.

Example:

// In your model
public function casts(): array
{
    return [
        'api_key' => 'encrypted',
    ]
}

Enter fullscreen mode Exit fullscreen mode
// Storing
$user->api_key = 'my-secret-key';
$user->save();

// Retrieving (auto-decrypted)
echo $user->api_key;

Enter fullscreen mode Exit fullscreen mode

Why it matters:

Even if the database is compromised, encrypted values are unreadable without the key.

Final Thoughts

Security isn’t a “set it and forget it” task — it’s an ongoing process.

By proactively addressing these six vulnerabilities, you can significantly reduce your Laravel application’s attack surface.

Top comments (2)

Collapse
 
parijke profile image
Paul Rijke

Looks like Burts youtube video

Collapse
 
williamdk profile image
Asaba William

Yeah, sure. As it's noted in the article: This guide covers six common Laravel security mistakes (based on the Laravel Security Guide video) and provides practical fixes with examples you can use today.