Laravel validation and custom rules in Inspector

Valerio Barbera
Graphic with Valerio’s avatar a heading reading Laravel validation and custom rules in Inspector with red abstract art on the right

Hi, I’m Valerio Barbera, software engineer, founder and CTO at Inspector.

Data validation is one of the fundamental features in any application. And it is something developers manipulate almost every day. The value a software provides to users is often a function of the quality of data it collects and provides.

Laravel ships with many predefined validation rules you can immediately use in your controllers. But working on the Inspector backend we have identified some aspects of validating incoming data that impact the security and reliability of our application.

Also, thanks to custom rules, I’ll show you how you can easily extend the validation layer of your app with new functionalities provided by external services.

I’ll start with some context to clarify the validation layer’s role in a back-end service. Then I’ll show you our implementations.

If you have recently started using Laravel you can read a quick overview in this previos article: What is Laravel framework, and why is it so popular?

Laravel Validation layer

Data integrity and validation are essential aspects of web development because they define the app’s state. If the data is wrong, the application will not behave correctly.

It’s always important to validate data before storing them in the database and before doing anything else.

In the Laravel request lifecycle, an HTTP request sent by a client goes through middleware first. Middleware deals with a mix of things between authentication and security.

Before the request enters the application, its data must be validated.

Illustration of Laravel’s validation layer with icons left to right of client computer, Lavarvel middleware, data validation, and the controller.

There are two ways to do data validation in Laravel: Inside the controllers or using Form requests.

Data Validation in controller

The easiest way of validation is performing it in the controller. At the start of each controller method, you can first validate data:

<?php
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
 
class UserController extends Controller
{
    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|min:3',
            'email' => 'required|email|min:6',
        ]);
 
        // here we know data are valid so we can pass them to database or other services
    }
}

Laravel will take care to return a 422 response code to the client if the data is not valid.

Data validation using Form requests

If your validation rules are too complex, you may want to encapsulate them in reusable classes. This approach will avoid messing up the controller.

Laravel provides the ability to wrap validation in a dedicated component called FormRequest.

First, create a form request:

php artisan make:request StoreUserRequest

Than move your validation logic inside the rules method of the request class:

<?php
 
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
 
class StoreUserRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }
 
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|string|min:3',
            'email' => 'required|email|min:6',
        ];
    }
}

You can type-hint this new request class in the controller method instead of the original Illuminate\Http\Request class, and remove the validation statement from the controller:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Requests\StoreUserRequest;
 
class UserController extends Controller
{
    public function store(StoreUserRequest $request)
    {
        // here we know data are valid so we can pass them to database or other services
    }
}

Custom validation rules

Laravel provided a well-developed validation layer. You can easily extend it by implementing custom rules to reuse in your code. Or you can increase the capability of your validation using external services.

Let me show you an example of one of the custom rules we implemented in Inspector.

First, create the class that represents a validation rule in Laravel:

php artisan make:rule SecurePassword

The idea is to verify if a password is in the list of well known insecure passwords. It will not pass the validation if it is, forcing the user to use a more secure string.

<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class SecurePassword implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return !in_array($value, [
            'picture1',
            'password',
            'password1',
            '12345678',
            '111111',
			
			...
        ]);
    }
    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The chosen password is unsecure. Try again with a less common string.';
    }
}

Integrate with external services

When exploring data validation, many SaaS services can bring new capabilities to your validation layer regarding the security and reliability of the data collected.

I recommend looking at apilayer.com, which provides excellent REST services to deal with data.

In Inspector, we use the mailboxlayer.com API to validate emails. The service can also detect fake email addresses, temporary addresses, and the actual existence of an email address using MX-Records and SMTP.

Add two configuration property to store the API key of the new service in the config/service.php file:

<?php
return [
    ...,
	
    'mailboxlayer' => [
        'key' => env('MAILBOXLAYER_KEY'),
    ],
	
];

Create the custom rule:

php artisan make:rule EmailSpam

Here is the complete code of the rule:

<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class EmailSpam implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param string $attribute
     * @param mixed $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        if (app()->environment('local')) {
            return true;
        }
        return !config('services.mailboxlayer.key') || $this->check($value);
    }
    /**
     * Perform email check.
     *
     * @param string $email
     * @return bool
     */
    protected function check(string $email): bool
    {
        try{
            $response = file_get_contents('https://apilayer.net/api/check?'.http_build_query([
                'access_key' => config('services.mailboxlayer.key'),
                'email' => '[mailbox-layer-account-email]',
                'smtp' => 1,
            ]));
            $response = json_decode($response, true);
            return $response['format_valid'] && !$response['disposable'];
        } catch (\Exception $exception) {
            report($exception);
            if (app()->environment('local')) {
                return false;
            }
            // Don't block production environment in case of apilayer error
            return true;
        }
    }
    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'Invalid email address.';
    }
}

Laravel validation Tips & Tricks

Validate borders

Based on my experience, I can suggest you always validate the minimum and the maximum sizes of the incoming fields .

Don’t wait for database errors that truncate too long strings. Instead, help your users understand the limits of each field by the error messages returned during data validation.

Ask for the current password

Every critical action should require password confirmation. For example, you should always prompt the user to type the current password to authorize actions that can compromise the account accessibility.

Examples include changing email and changing password.

This feature will improve security because even having physical access to the computer with the Inspector dashboard opened on the screen; a malicious user can’t change access credentials without knowing the current password. So, he can’t shut you out.

Here is our implementation of the current password verification:

<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class CurrentPassword implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return Hash::check($value, Auth::user()->password);
    }
    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'Your current password is incorrect.';
    }
}

Laravel application monitoring

If you found this post interesting and want to drastically change your developers’ life for the better, you can give Inspector a try.

Inspector is an easy to use Code Execution Monitoring tool that helps developers to identify bugs and bottlenecks in their application automatically. Before customers do.

screenshot inspector code monitoring timeline

It is completely code-driven. You won’t have to install anything at the server level or make complex configurations in your cloud infrastructure.

It works with a lightweight software library that you can install in your application like any other dependency. You can try the Laravel package, it’s free.

Create an account, or visit our website for more information: https://inspector.dev/laravel

Related Posts

What is Autoscaling and how we use it to scale Inspector

A nightmare (or a dream 🙂 ) for any software developer is an unexpected high influx of traffic or a sudden change in usage patterns that cause an application crash due to lack of computing resources. Autoscaling is a critical service for any successful application to provide maximum performance and stability at all times. Hi,

cloud costs savings with code execution monitoring

How to save thousands of dollars in cloud costs with Code Execution Monitoring

Hi, I’m Valerio, software engineer, co-founder and CTO at Inspector. In this article I want to show you a monitoring strategy to help you save thousands of dollars a year on cloud costs. The typical scenario where you can get the most payback from this strategy is when your application runs on multiple servers. If

The 5 Best Application Monitoring Tools for 2022

When it comes to application monitoring software, there are some great tools available to make our lives easier. We’ve compiled a list of the very best. No offense intended but the truth is customers are no longer interested in what you have to do to make your product work when they need it. They only

How to build scalable applications

Get the e-book about the Inspector scalability journey.