Security Tip: Scoping Bindings

[Tip#23] Because who doesn't love to scope their bindings?

Security Tip: Scoping Bindings

If you use Laravel’s Implicit Route Bindings, you’ll most likely end up with a route that looks like this:

Route::get(
    '/projects/{project}/plans/{plan}', 
    function (Project $project, Plan $plan) {
        abort_unless($plan->project()->is($project), 404);

        // ...
    }
);

While it's easy to validate that the Plan is owned by the Project, you still need to remember to add it to your action, and it can look a bit ugly...

💡
Most all of the vulnerabilities I’ve discovered have come about because the developer forgot to include a single line of code to perform authentication, authorisation, or validation. Usually with the exact line required already written for another action in the same controller.

Luckily for us, Laravel has our backs (yet again). There is a handy little method in Laravel’s Routing system called scopeBindings(), which we can use to tell Laravel to automatically scope implicitly bound models.

Check it out:

Route::get(
    '/projects/{project}/plans/{plan}', 
    function (Project $project, Plan $plan) {
        // ...
    }
)->scopeBindings();

Laravel will automatically attempt to load the “child” binding from a relationship on the “parent” binding listed before it. In other words, in our example it will look for a matching Plan model from the plans relationship on the Project model. If the child binding isn’t found, it’ll throw a 404.

This is the same behaviour as our code above, but without that ugly abort_unless().

The best bit is, since it’s a route method we can use it in groups too! This solves the “forgetting to add it” problem, since you’ll typically group your routes and automatically add them into our scoped group.

Route::scopeBindings()
    ->group(function () {
        Route::get('/projects/{project}/plans/{plan}', ...);
        Route::get('/projects/{project}/plans/{plan}/edit', ...);
        Route::get('/projects/{project}/plans/{plan}/export', ...);
        // ...
});

Nice and simple, just the way we love it in Laravel!


If you found this security tip useful, subscribe to get weekly Security Tips straight to your inbox. Upgrade to a premium subscription for exclusive monthly In Depth articles, or drop a coin in the tip jar to show your support.

Looking for a Laravel Security Audit / Penetration Test, or a budget-friendly Security Review? Feel free to reach out! You can also connect with me on Bluesky, or other socials. And don’t miss Practical Laravel Security, my interactive course designed to boost your Laravel security skills.