Security Tip: Leaking Data After Changes

[Tip#35] It's easy to make innocent changes to one part of your app and forget to check how that flows into other parts of your app

Security Tip: Leaking Data After Changes

Imagine the following scenario:

Your application has a small number of basic routes and receive and return information. You’ve fully configured your hidden attributes on your models, and everything is working nicely.

One of your actions looks like this:

public function update(Request $request, Model $model) 
{
    $data = $request->validate([...]);
    $model = $model->update($data);
    return $model;
}

Now, imagine you come along later and starting adding more complexity to your app. Maybe you add in an admin section, some more columns to your database tables, as well as new routes and features.

But the two bits of code you haven’t touched are: the controller action above and the list of hidden fields on your model.

Suddenly your old untouched update() method is leaking sensitive data from the new DB columns you added for admin purposes! 😱

Now, you could argue the fix here is easy: “just remember to update the hidden property on your models when you add fields.” But that requires you to remember, and what if your new admin section needs to use those fields and that’s why you didn’t add them to your hidden property?

Ultimately, the problem is you’re relying on model attributes visibility being global across your whole app. This may work in smaller apps, but as your app grows, you’ll start to have sections with different permission levels that need to return and use different values for different users. At this point, global permissions are no longer useful.

A better way to control attribute permissions is on each route that returns data!

Use something like the only() method on models, or setVisible() on controllers, or maybe pass the collection/array through something like array_map() and manually extract the attributes you need.

Something like this would’ve protected our route:

public function update(Request $request, Model $model) {
    $data = $request->validate([...]);
    $model->update($data);

    return $model->only(['field1', 'field2']);
}

This way, changes to other parts of your app won’t risk leaking data in older untouched routes. Keeping your data secure. 🙂


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.

When was the last time you had a penetration test? Book a Laravel Security Audit and Penetration Test, or a budget-friendly Security Review!

You can also connect with me on Bluesky, or other socials, and check out Practical Laravel Security, my interactive course designed to boost your Laravel security skills.