Security Tip: Retrieving Request Values

[Tip#40] Let's complete the set of request input helpers and their security implications

Security Tip: Retrieving Request Values

Greetings my friends! If you keep track of my creative email schedule1, you might have noticed that this one is a few days late. It’s also not the In Depth that was promised a few weeks ago. We had a family emergency last week that encompassed everything and I’m only now recovering and catching up on everything. So rather than try to rush out an In Depth, I’ve pushed it back a week and we’re covering another Security Tip this week.

This week’s tip is an extension on last week’s tip on Casting Request Values. We looked at methods for returning user input as specific types. This week we’re looking at the different methods of pulling user input safely, to avoid mass-assignment vulnerabilities, and other such injection and manipulation attacks.

👉 Security Audits: Want me to hack your app and help you improve your security? 🕵️

Looking to learn more?
Security Tip #24: Use Route Groups
▶️ In Depth #9: Signed URLs

Retrieving Request Values

Laravel’s Request object2 (`Illuminate\Http\Request`) includes a number of methods for extracting user input. Last week we covered some individual methods, and this time we’re covering the array/collection methods.

public function validate(array $rules = [], array $messages = [], array $attributes = []): array;

My favourite approach: Validates the user input and, if successful, returns an array of only validated input. Usable in most scenarios. 😉

public function only($keys): array;

Returns only the input values with the specified keys as an array. This allows you to be sure that only the values you intend are returned, avoiding mass-assignment vulnerabilities.

Note that if an input value doesn’t exist, it won’t be included in the output array.

public function collect($key = null): \Illuminate\Support\Collection;

Returns the user input in a Laravel Collection, rather than an array. Note this carries the same risks as the `all()` method.

You can also specify a single key via the `$key` parameter to cast that specific input value as a Collection, or an array of keys to return a Collection with only those keys from the user input.

public function all($keys = null): array;

Use this method carefully!

Let’s get this one out of the way first. Without the `$keys` parameter, it returns all of the user submitted data, regardless of what it is. This makes it trivial to introduce mass-assignment vulnerabilities into your apps3.

If you pass the `$keys` parameter, it will return an array with every key you specify, even if it doesn’t exist in the user input. The value will be `null` instead. This makes the method safe to use, although I would suggest using `only()` instead so the parameters cannot be accidently removed.

public function except($keys): array;

The opposite of `only($keys)`. Blocks specific input values from being returned in an array. I can see a use in some situations, but like any blocklist, you need to be careful of what could be included.

As I said above, all of these methods are available on the `Illuminate\Http\Request` object, which you’ll typically have injected into your Controller action, or you use the `Request` Facade, or `request()` helper function:

use \Illuminate\Http\Request;

public function edit(Request $request): View
    $data = $request->validate([...]);
    // ...
use \Illuminate\Support\Facades\Request;

public function show(Model $model): View
    $data = Request::only([...]);
    // ...
public function show(Model $model): View
    $data = request()->collect([...]);
    // ...

My Recommendations

Every use case is different, but my recommendation is to only use `validate($rules)`, `only($keys)`, and `collect($keys)`.

This gives you full control over the user input you accept into your app, significantly reducing the possibility of mass-assignment vulnerabilities4.

Between those three methods, and the ones we looked last time, you should have the flexibility you need to safely work with user input.

Bonus Methods

As an bonus, there are two more methods you can use:

public function query($key = null, $default = null);
public function post($key = null, $default = null);

These will only input values only from the query string or post data respectively. This allows you to control exactly where the data comes from, if that’s important for your application.

  1. I talk about the schedule occasionally, but no one has ever mentioned it. So I suspect it’s just me. 🤣

  2. See:

  3. I really need to write an In Depth about these!

  4. Unless you accidently allow keys you shouldn’t…