Laravel Validaton: All Your Base Are Belong To Us

Will Bowman
asked.io
Published in
4 min readOct 1, 2017

We all use Laravels Validation feature to ensure we have the required input and it matches some sort of rule. The available rules cover plenty of situations but they fail to cover one of the most important rules, input Sanitization.

TL;DR

A typical validator

Laravels Auth system comes with a simple User model that provides validation of the users details before it’s inserted into your database. We see it like:

return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);

These rules will ensure the data we need is there and of the desired length and type.

That’s it, we’re done! Nothing else to check, required and max made sure our data is safe, right?

But wait, there’s more

Don’t go yet, come back and clam your prize. A dirty database.

That’s a really cool user name there isn’t it?

Since you only required the name and made sure it was 255 chars or less the “user” was allowed to input anything they wanted, in this case a pretty ‘hello world’ alert loop that crashes Firefox.

Buy now and we’ll double your order

Double up your dirty database with any type of unescaped output of the users name and let the magic begin.

If as in magic you mean things users will exploit.

The User Auth example escapes all of the strings with {{ }} before displaying to the user so there wont be much magic going on with that example.

If you defined a variable, even escaped, within a <script> or html <=””> tag your ticket to the magic show is valid, so watch out. Even escaped outputs can trigger XSS and other attacks when used improperly.

It’s 2016, you probably have an API

Did you do any validation on the output from the API? Most APIs for Laravel allow you to transform your data, if you didn’t sanitize your output in the transformation then what the user provided is sent to your frontend.

Did you sanitize the output in your frontend? Well if you didn’t, congrats, your entrance to the magic show has been granted. Be prepared to be amused.

Hurry, while supplies last

You can easily close the door on exploits and dirty databases by properly validating and sanitizing your users input.

Whitelisting, mmmm.

If you validate user input against a whitelist you ensure you get only the data you want. A simple alpha_num or regexp can often do the trick.

This works great when you need to allow HTML or need a very specific type of value.

It’s not very practical for all of your fields.

For those generic fields that can accept almost anything (like utf8 and unicode) another solution may work better for you. Sanitizing.

Sanitizing the users input will help prevent the user from inserting possible exploits and mucking up your database.

It’s not as fool proof as a whitelist but it is the most recommended method I’ve seen over the years.

Many people sanitize their inputs with htmlspecialchars, some like strip_tags but I prefer HTMLPurifier.

Note: ‘mews/purifier’ does allow some HTML in the inputs by default, you can adjust this in its config.

Your order has shipped

Unbox it and start putting it together. The inputs are yours so you’ll need to decide what method to choose.

composer require daylerees/sanitizer
composer require mews/purifier

app/Http/routes.php

<?php
Route::get('/', function () {
$validator = app('validator')->make(request()->all(), [
'title' => 'filter:clean|required|max:255',
'body' => 'required',
]);

return $validator->fails() ? $validator->errors() : 'pass';
});

app/Providers/AppServiceProvider.php

<?phpnamespace App\Providers;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider
{
public function boot()
{
app('validator')->extend('filter', function ($attribute, $value, $parameters, $validator) {
$data = [$attribute => $value];
app('sanitizer')->sanitize([$attribute => $parameters], $data);
$replace = array_merge($validator->getData(), [$attribute => $data[$attribute]]);
request()->replace($replace);
$validator->setData($replace);
return true;
});
}
public function register()
{
app()->register('Rees\Sanitizer\SanitizerServiceProvider');
app()->register('Mews\Purifier\PurifierServiceProvider');
}
}

Don’t return this item to the store, we can help

If you can’t tell what’s going on, no worries.

The ServiceProvider is extending the validator class with a ‘filter’ option that can be accessed in your validation rules:

'title' => 'filter:clean|required|max:255',

You can use multiple filters:

'title' => 'filter:clean,htmlspecialchars|required|max:255',

You can create custom filters:

app('sanitizer')->register('sanitize_string', function ($field) {
return htmlspecialchars($field);
});
$validator = app('validator')->make(request()->all(), [
'title' => 'filter:sanitize_string|required|max:255',
'body' => 'required',
]);

You can use it any way Dayle intended with his Sanitizer package.

Conclusion

Sanitizing inputs in Laravel is far from a new topic. It’s been talked about recently and at one point even had some code started.

I found plenty of articles talking about it, new and old, but they all had some extra steps or classes, nothing short and to the point.

I don’t want to define more rules. I want to sanitize when I validate and in the same manner as validation.

The ideas from #42 lead me to using the Sanitizer package with Validator, a simple combination that allows me to do pretty much anything I want with my inputs.

--

--