Security Tip: Watch out for Resource Authorisation

[Tip#50] Watch out when you mix Resource Controllers and Authorisation with custom Controller Actions and custom routes... you may find you're lacking authorisation without realising it!

Security Tip: Watch out for Resource Authorisation

During a recent security audit, I discovered heavy use of Resource Controllers and Resource Controller Authorisation. It’s a common pattern and one I’m very familiar with, but there was a twist this time…

Take a look at their routes (or at least a variant that protects the guilty):

Route::get('/books/all', [BookController::class, 'all']);
Route::get('/books/export', [BookController::class, 'export']);
Route::resource('book', BookController::class);

And the controller looked like this:

class BookController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Book::class, 'book');
    }

    public function all(Request $request)
    {
        return ...;
    }

    // Typical resource actions

    public function export(Request $request)
    {
        return ...;
    }
}

Now I can see exactly what they were thinking here: authorizeResource() handles authorisation for controller actions, so they don’t need any manual authorize() calls. But guess what happens when a non-standard action is used?

Authorisation is skipped entirely, allowing any authenticated user full access to these extra endpoints! 😱

In my example it’s called Books, but what if it was Users, or Patients, or Contracts? You can quickly see the problem here, as any authenticated user can access any of these unprotected routes.

My Recommendation: Avoid mixing authorisation systems and either use resource controller authorisation without custom actions, or authorise on every action. It’s more tedious, but makes you less likely to forget authorisation. Or, as I’ve said before, if you put authorisation on your routes via middleware, it's harder to forget and easier to review.

During my audit I was able to create a free trial user, and discover (via Ziggy) unprotected routes that provided me with their entire client database, all via an unprotected export route in their admin tools! Super easy to find and exploit, and they completely missed it.

This is the reason I do my Security Audits - issues like these can be so easily overlooked by dev teams, who are so used to seeing their own code and making assumptions without realising it. Often it takes an independent 3rd party with fresh eyes to look at your code before vulnerabilities are discovered, and it’s far better for that 3rd party to be a friendly hacker working for you than a malicious hacker working against you.


If you've made it this far, please share this Security Tip and Securing Laravel with your Laravel friends, colleagues, and enemies! The more folks we have learning about security, the more secure code will be written, and the safer the whole community and our users will be. Also, if you tag me I'll give it a retweet.

Find me on: Pinkary, Twitter, Mastodon, LinkedIn, & Threads.