Security Tip: Validating (Secure) URLs!

[Tip #90] Did you know Laravel's URL validator lets you control which protocols you accept? Here's my recommendation...

Security Tip: Validating (Secure) URLs!

Laravel's Validator is an essential piece to preventing unexpected and malicious inputs from making their way into our apps. It includes a long list of built-in validation rules that handle most common scenarios, with options to tweak these rules to refine the validation needed.

Today, we're taking a look at the url validator, which verifies input fields contain valid URLs, and lets you define the allowed protocols for the URLs provided.

By default, the url validator allows anything that looks like a URL:

Validator::make(
    ['link' => "https://evilhacker.dev"], // input
    ['link' => 'url']                     // rule
)->passes();
// => TRUE

Validator::make(
    ['link' => "steam://evilhacker.dev"], // input
    ['link' => 'url']                     // rule
)->passes();
// => TRUE

While this may not be an issue in some cases, there are many situations where the user would not expect to click on a user-submitted link in your app and be redirected to some external app.

To get around this, you can tell the validator what protocols are allowed by the validator, for example, we can require http and https URLs like this:

// INVALID LINK

Validator::make(
    ['link' => "steam://evilhacker.dev"], // input
    ['link' => 'url:http,https']          // rule
)->passes();
// => FALSE

// VALID LINK

Validator::make(
    ['link' => 'http://evilhacker.dev'], // input
    ['link' => 'url:http,https']         // rule
)->passes();
// => TRUE

My recommendation is to take it a step further, and require HTTPS URLs for all user input, if possible.

This will have the benefit of ensuring all user-submitted links are for secure (i.e. encrypted) sites, and will keep any your users safe from Downgrade attacks, etc. This may be incredibly important if your app deals with sensitive or private information (i.e. PII, PHI, etc), and needs to send users to external sites that also need to interact with this data.

// INVALID LINK

Validator::make(
    ['link' => "http://evilhacker.dev"], // input
    ['link' => 'url:https']              // rule
)->passes();
// => FALSE

// VALID LINK

Validator::make(
    ['link' => 'https://evilhacker.dev'], // input
    ['link' => 'url:https']               // rule
)->passes();
// => TRUE
Depending on your business needs, it may not be possible to enforce HTTPS-only for user submitted URLs, but it's worth having that discussion and enforcing it if you can.