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 models1, 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. 🙂


👉 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! 🕵️

👉 When was your last security audit or penetration test? Book in a Laravel Security Audit and Penetration Test today! 🕵️


  1. We’ve covered these before in Security Tip #8: Sensitive Model Attributes.