DEV Community

David Carr
David Carr

Posted on • Originally published at dcblog.dev on

Laravel Security Headers

Laravel Security Headers

This weekend, I changed the design of this blog whilst doing so I wanted to add the security headers for content security policies, these tell the application what it can and cannot run, There's a great website called https://securityheaders.com which will scan a URL and tell you what your level is.

If you have no headers set up you'll get an F grade, which is bad!

Before I started my rating:

an F grade website

Once I finished I have an A rating:

A rating

For detailed information about security headers read Daniel Dušek blog that explains this really well https://danieldusek.com/enabling-security-headers-for-your-website-with-php-and-laravel.html

Still here? great I'll go over what I've done.

I created a middleware class called SecurityHeaders.php inside App\Http\Middleware of my Laravel application

Add this middleware to the Middleware group inside App\Http\Kernal.php


protected $middleware = [
    // \App\Http\Middleware\TrustHosts::class,
    \App\Http\Middleware\TrustProxies::class,
    \Fruitcake\Cors\HandleCors::class,
    \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \App\Http\Middleware\SecurityHeaders::class,
];
Enter fullscreen mode Exit fullscreen mode

Set the headers to be turned off, this provide would be attackers information about the server, you don't need to advertise these to better to turn them off.


private $unwantedHeaders = ['X-Powered-By', 'server', 'Server'];
Enter fullscreen mode Exit fullscreen mode

Referrer Policy

Sets how much information should be sent with requests, in my case I chose the option to not send the referer header for requests to less secure destinations.


$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
Enter fullscreen mode Exit fullscreen mode

XSS Protection

Stops loading of pages when they detect reflected cross-site scripting (XSS) attacks

I set: Enables XSS filtering. Rather than sanitizing the page, the browser will prevent rendering of the page if an attack is detected.


$response->headers->set('X-XSS-Protection', '1; mode=block');
Enter fullscreen mode Exit fullscreen mode

Strict Transport Security

informs browsers that the site should only be accessed using HTTPS


$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
Enter fullscreen mode Exit fullscreen mode

Content Security Policy

The content security policy sets weather a browser can run JS / CSS on page load.

You will want to either det a URL specifically or a wildcard like *.domain to allow the subdomain of the given domain to run


$response->headers->set('Content-Security-Policy', "default-src 'self'; script-src 'self' platform.twitter.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' * data:; font-src 'self' data: ; connect-src 'self'; media-src 'self'; frame-src 'self' platform.twitter.com github.com *.youtube.com *.vimeo.com; object-src 'none'; base-uri 'self'; report-uri ");
Enter fullscreen mode Exit fullscreen mode

Expect-CT

The Expect-CT header lets sites opt in to reporting and/or enforcement of Certificate Transparency requirements


$response->headers->set('Expect-CT', 'enforce, max-age=30');
Enter fullscreen mode Exit fullscreen mode

Permissions-Policy

Sets what permissions the device load the page is given


$response->headers->set('Permissions-Policy', 'autoplay=(self), camera=(), encrypted-media=(self), fullscreen=(), geolocation=(self), gyroscope=(self), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=(self), usb=()');
Enter fullscreen mode Exit fullscreen mode

The complete class:

With this class in place upload your changes to your server and re-run https://securityheaders.com


<?php

namespace App\Http\Middleware;

use Closure;

class SecurityHeaders
{
    private $unwantedHeaders = ['X-Powered-By', 'server', 'Server'];

    /**
     * @param $request
     * @param Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        if (!app()->environment('testing')) {
            $response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
            $response->headers->set('X-XSS-Protection', '1; mode=block');
            $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
            $response->headers->set('Content-Security-Policy', "default-src 'self'; script-src 'self' platform.twitter.com plausible.io utteranc.es *.cloudflare.com 'unsafe-inline' 'unsafe-eval' plausible.io/js/plausible.js utteranc.es/client.js; style-src 'self' *.cloudflare.com 'unsafe-inline'; img-src 'self' * data:; font-src 'self' data: ; connect-src 'self' plausible.io/api/event; media-src 'self'; frame-src 'self' platform.twitter.com plausible.io utteranc.es github.com *.youtube.com *.vimeo.com; object-src 'none'; base-uri 'self';");
            $response->headers->set('Expect-CT', 'enforce, max-age=30');
            $response->headers->set('Permissions-Policy', 'autoplay=(self), camera=(), encrypted-media=(self), fullscreen=(), geolocation=(self), gyroscope=(self), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=(self), usb=()');
            $response->headers->set('Access-Control-Allow-Origin', '*');
            $response->headers->set('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
            $response->headers->set('Access-Control-Allow-Headers', 'Content-Type,Authorization,X-Requested-With,X-CSRF-Token');

            $this->removeUnwantedHeaders($this->unwantedHeaders);
        }

        return $response;
    }

    /**
     * @param $headers
     */
    private function removeUnwantedHeaders($headers): void
    {
        foreach ($headers as $header) {
            header_remove($header);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)