Enforcing valid data access from a form request object

Blog post
by Tim MacDonald on the

Making sure you validate incoming request data from the user is crucial for security and ensuring your app stays in a valid state. Validating data is easy peasy lemon squeezy with Laravel's validation rules in combination with form request objects.

Another thing Laravel does is make it super easy to access request input. A form request object extends Laravel's request object, so accessing a title input is as easy as $request->title, but there lays the potential issue. It can be too easy, let's take a look at why.

Creating models from request data

In your controller you have probably done something like this to create a model:

public function store(PostRequest $request)
{
    Post::create([
        'title' => $request->title,
        'content' => $request->content
    ]);

    // or

    Post::create($request->only(['title', 'content']));
}

Both a perfectly valid options and I've been doing this myself in my projects. However, recently I was thinking it is very possible to access input from the form that has not been validated. Looking at this example, how do I know those are validated inputs?

When I'm in my controller I do not know if the form has validated the title or content input...and that scares me. Sure, I'm a big kid and should trust that I did validate the data, but with such an important piece of the puzzle I don't like to take any risks.

Enforcing valid data access

Let's wrap ourselves up in a security blanket so we can sleep easy. In a form request object we set our rules as per normal:

public function rules()
{
    return [
        'title' => 'required|string|max:255',
        'content' => 'required|string',
    ];
}

and then we add a new method to our request object to keep ourselves in check:

public function validatedInputs()
{
    return $this->only(array_keys($this->rules()));
}

This method is getting all the rules array keys and only returning the input of those keys. Now in our controller we know that our input has been validated by our rules.

public function store(PostRequest $request)
{
    Post::create($request->validatedInputs());
}

😌 that makes me feel better.

Notes

  • This could of course be extended to include other helpful methods such as validatedInput($key, $default), onlyValidatedInput($keys) etc.
  • This works great with flat arrays, but once you you are creating more complex rules such as the following, things start to get a little hairy, but by then you're probably doing other trickery anyway.
public function rules()
{
    return [
        'users.*.email' => 'required|email',
    ];
}

Update

I attempted to PR this into the core, however it did not get merged. Thankfully though Joseph Silber‏ added a much more comprehensive set of PRs to add this functionality to the core which will be baked into the upcoming Laravel release 5.5. Very cool - looking forward to trying it!