Security Tip: Be Careful of Auth Helpers!

[Tip#20] Laravel's helpers are great, but make sure you know everything they do before you use them.

Security Tip: Be Careful of Auth Helpers!

Laravel’s Authentication system is incredibly powerful and coupled with the Auth Facade, it makes it trivial to access the logged in user where you need to. However, you also need to be careful - this power and ease also opens up some huge risks you need to be aware of.

Within the normal login request flow, you’ll authenticate the user once and they’ll still be logged in to each subsequent request, allowing you to access the user model easily via the Auth Facade. But what if you have a special public or private URL that provides access to something within the user context for a single request, but isn’t accessed by the user? You still need to access the user model easily but you don’t want their session persisted into the subsequent requests.

Consider a draft post URL that the author can provide for unauthenticated users to view the draft. It’s tempting to reuse code which calls the Auth Facade, but then you’ll need to effectively log the user to load the Auth Facade. So you may find yourself doing something like this:

public function view(Post $post) 
{
    Auth::loginUsingId($post->user_id);

    return view('posts.preview', [
        'post' => (new PostEditor($post))->readOnly(),
    ]);
}

It seems simple enough - you’re temporarily logging the user in so the post can be loaded in the user context, so you can reuse the same code.

Makes sense, right?

Nope!

The problem is Auth::loginUsingId() logs the user in and initiates a session. The visitor is now logged in and will have full access to the user account on subsequent requests.

You never want to do this. Don’t even consider doing it and then immediately logging out (session info and cookies may still be persisted)! So please just don’t do it!

I would highly recommend not using the Auth Facade in this situation at all, instead, you should pass the User model around where you need it. It’s much safer as you’ll know there is no chance of anything from the user persisting.

public function view(Post $post) 
{
    $user = $post->user;

    return view('posts.preview', [
        'user' => $user,
        'post' => (new PostEditor($post, $user))->readOnly(),
    ]);
}
If you do need to use the Auth Facade to load the user within these special requests, you can safely use the Auth::onceUsingId($userId) function. It loads the specified model as the current user for the current request only.

TL;DR → Don’t use Auth::loginUsingId() on guest URLs, it sets up a full authentication session and you’ll be logging everyone in as that user.


🕵️
When was your last security audit or penetration test? Book in a Laravel Security Audit and Penetration Test today!
🥷
Looking to dive deeper into Laravel security? Check out Practical Laravel Security, my hands-on security course that uses interactive hacking challenges to teach you about how vulnerabilities work, so you can avoid them in your own code!