blueclock / laravel-csp

Set content security policy headers in a Laravel app

Home Page:https://spatie.be/en/opensource/laravel

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WORK IN PROGRESS, DO NOT USE YET

Set content security policy headers in a Laravel app

Latest Version on Packagist Build Status StyleCI Quality Score Total Downloads

By default all scripts on a webpage are allowed to fetch from and send data to any site they want. This can be a security problem. Imagine one of your JavaScript dependencies sends all keystrokes, including passwords, to a third party website.

It's very easy for someone to hide this malicious behaviour, making it nearly impossible for you to detect it (unless you manually read all the JavaScript code on your site). To feel why you really need to set content security policy headers read this excellent blog post by David Gilbertson.

The solution to this problem is setting Content Security Policy headers. These headers dictate which sites your site is allowed to contact. This package makes it easy for you to set the right headers.

This readme does not aim to fully explain all the possible usages of CSP and it's directives. It's highly recommened that you read Mozilla's documentation on the Content Security Policy) before using this package.

Installation

You can install the package via composer:

composer require spatie/laravel-csp

You can publish the config-file with:

php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider" --tag="config"

This is the contents of the file which will be published at config/csp.php:

return [

    /*
     * A CSP profile will determine which CSP headers will be set. A valid CSP profile is
     * any class that extends `Spatie\Csp\Profiles\Profile`
     */
    'profile' => Spatie\Csp\Profiles\Basic::class,

    /*
     * This profile which will be put in report only mode. This is great for testing out
     * a new profile or changes to existing csp policy without breaking anyting.
     */
    'report_only_profile' => '',

    /*
     * All violations against the CSP policy will be reported to this url.
     * A great service you could use for this is https://report-uri.com/
     *
     * You can override this setting by calling `reportTo` on your profile.
     */
    'report_uri' => env('CSP_REPORT_URI', ''),

    /*
     * Headers will only be added if this setting is set to true.
     */
    'enabled' => env('CSP_ENABLED', true),

    /*
     * The class responsible for generating the nonces used in inline tags and headers.
     */
    'nonce_generator' => Spatie\Csp\Nonce\RandomString::class,
];

You can add CSP headers to all responses of your app by registering Spatie\Csp\AddCspHeaders::class in the http kernel.

// app/Http/Kernel.php

...

protected $middlewareGroups = [
   'web' => [
       ...
       \Spatie\Csp\AddCspHeaders::class,
   ],

Alternatively you can apply the middelware on the route or route group level.

// in a routes file

Route::get('my-page', 'MyController')->middleware(Spatie\Csp\AddCspHeaders::class);

You can also pass a profile class as a parameter to the middleware:

// in a routes file

Route::get('my-page', 'MyController')->middleware(Spatie\Csp\AddCspHeaders::class . ':' . MyProfile::class);

The given profile will override the one configured in the config file for that specific route or group of routes.

Usage

This package allows you to define CSP profiles. A CSP profile determines which CSP directives will be set in the headers of the response.

An example of a CSP directive is script-src. If this has the value 'self' www.google.com then your site can only load scripts from it's own domain of www.google.com. You'll find a list with all CSP directives at Mozilla's excellent developer site.

According to the spec certain directive values need to be surrounded by quotes. Examples of this are 'self', 'none' and 'unsafe-inline'. When using addDirective function you're not required to surround the directive value with quotes manually. We will automatically add quotes.

// in a profile
...
   ->addDirective(Directive::SCRIPT, 'self') // will output `'self'` when outputting headers
...

Creating profiles

In the profile key of the csp config file is set to \Spatie\Csp\Profiles\Basic::class by default. This class allows your site to only use images, scripts, form actions of your own site. This is how the class looks like.

namespace Spatie\Csp\Profiles;

use Spatie\Csp\Directive;

class Basic extends Profile
{
    public function configure()
    {
        $this
            ->addDirective(Directive::CONNECT, 'self')
            ->addDirective(Directive::DEFAULT, 'self')
            ->addDirective(Directive::FORM_ACTION, 'self')
            ->addDirective(Directive::IMG, 'self')
            ->addDirective(Directive::MEDIA, 'self')
            ->addDirective(Directive::SCRIPT, 'self')
            ->addDirective(Directive::STYLE, 'self')
            ->addDirective(Directive::OBJECT, 'none')
            ->addNonceForDirective(Directive::SCRIPT)
            ->addNonceForDirective(Directive::STYLE);
    }
}

You can allow fetching scripts from www.google.com by extending this class:

namespace App\Services\CspProfiles;

use Spatie\Csp\Directive;
use Spatie\Csp\Profiles\Profile;

class MyCustomProfile extends Profile
{
    public function configure()
    {
        parent::configure();
        
        $this->addDirective(Directive::SCRIPT, 'www.google.com');
    }
}

Don't forget to set the profile key in the csp config file to the class name of your profile (in this case it would be App\Services\CspProfiles\MyCustomProfile).

Using inline scripts and styles

When using CSP you must specifically allow the use of inline scripts or styles. The recommended way of doing that with this package is to use a nonce. A nonce is a number that iss unique per request. The nonce must be specified in the CSP headers and in an attribute on the html tag. This way an attacker has no way of injecting malious scripts or styles.

First you must add the nonce to the right directives in your profile:

// in a profile

public function configure()
  {
      $this
        ->addDirective(Directive::SCRIPT, 'self')
        ->addDirective(Directive::STYLE, 'self')
        ->addNonceForDirective(Directive::SCRIPT)
        ->addNonceForDirective(Directive::STYLE)
        ...
}

Next you must add the nonce to the html:

{{-- in a view --}}
<style nonce="{{ cspNonce() }}">
   ...
</style>

<script nonce="{{ cspNonce() }}">
   ...
</script>

There are few other options to use inline styles and scripts. Take a look at the CSP docs on the Mozilla developer site to know more.

Reporting CSP errors

In the browser

Instead of downright blocking all violations you can put a profile in report only mode. In this case all requests will be made, but you'll see all violations will be displaying in your favourite browser's console.

To put a profile in report only mode just call reportOnly() in the configure() function of a report:

    public function configure()
    {
        parent::configure();
        
        $this->reportOnly();
    }

To an external url

Any violations against to the policy can be reported to a given url. You can set that url in the report_uri key of the csp config file. A great service that is specifically built for handling these violation reports is http://report-uri.io/.

Using multipe profiles

To test out changes to your CSP policy you can specify a second profile in the report_only_profile in the csp config key. The profile specified in profile will be enforced, the one in report_only_profile will not. This is great for testing out a new profile or changes to existing CSP policy without breaking anyting.

Testing

You can run all the tests with:

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email freek@spatie.be instead of using the issue tracker.

Postcardware

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Samberstraat 69D, 2060 Antwerp, Belgium.

We publish all received postcards on our company website.

Credits

Support us

Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects on our website.

Does your business depend on our contributions? Reach out and support us on Patreon. All pledges will be dedicated to allocating workforce on maintenance and new awesome stuff.

License

The MIT License (MIT). Please see License File for more information.

About

Set content security policy headers in a Laravel app

https://spatie.be/en/opensource/laravel

License:MIT License


Languages

Language:PHP 100.0%