All Projects → spatie → Laravel Csp

spatie / Laravel Csp

Licence: mit
Set content security policy headers in a Laravel app

Projects that are alternatives of or similar to Laravel Csp

Secure Headers
PHP Secure Headers
Stars: ✭ 379 (-2.32%)
Mutual labels:  laravel, csp
Cors
🔮Supported(Laravel/Lumen/PSR-15/Swoft/Slim/ThinkPHP) - PHP CORS (Cross-origin resource sharing) middleware.
Stars: ✭ 266 (-31.44%)
Mutual labels:  laravel, request
Laravel Missing Page Redirector
Redirect missing pages in your Laravel application
Stars: ✭ 378 (-2.58%)
Mutual labels:  laravel
Polr
🚡 A modern, powerful, and robust URL shortener
Stars: ✭ 4,147 (+968.81%)
Mutual labels:  laravel
Eloquent Taggable
Easily add the ability to tag your Eloquent models in Laravel.
Stars: ✭ 382 (-1.55%)
Mutual labels:  laravel
Coastercms
The repository for Coaster CMS (coastercms.org), a full featured, Laravel based Content Management System
Stars: ✭ 380 (-2.06%)
Mutual labels:  laravel
Laravel Trix
Configurable Basecamp Trix Editor (WYSIWYG) delivered to your laravel application
Stars: ✭ 383 (-1.29%)
Mutual labels:  laravel
Docker Stacks
DECK is a powerful and high performant local web development studio unlike any other.
Stars: ✭ 376 (-3.09%)
Mutual labels:  laravel
Laravel Model Cleanup
Clean up unneeded records
Stars: ✭ 388 (+0%)
Mutual labels:  laravel
Laravel Flash
A lightweight package to flash messages
Stars: ✭ 382 (-1.55%)
Mutual labels:  laravel
Crater
Open Source Invoicing Solution for Individuals & Businesses
Stars: ✭ 4,897 (+1162.11%)
Mutual labels:  laravel
Google Maps
Collection of Google Maps API Web Services for Laravel
Stars: ✭ 380 (-2.06%)
Mutual labels:  laravel
Bitcoin Core
A modern Bitcoin Core REST and RPC client.
Stars: ✭ 379 (-2.32%)
Mutual labels:  request
Laravel Relationship Events
Missing relationship events for Laravel
Stars: ✭ 383 (-1.29%)
Mutual labels:  laravel
Laravel Websockets
Websockets for Laravel. Done right.
Stars: ✭ 4,157 (+971.39%)
Mutual labels:  laravel
Collision
💥 Collision is a beautiful error reporting tool for command-line applications
Stars: ✭ 3,993 (+929.12%)
Mutual labels:  laravel
Laravel Firebase
A Laravel package for the Firebase PHP Admin SDK
Stars: ✭ 369 (-4.9%)
Mutual labels:  laravel
Enlightn
Your performance & security consultant, an artisan command away.
Stars: ✭ 378 (-2.58%)
Mutual labels:  laravel
I Educar
Lançando o maior software livre de educação do Brasil!
Stars: ✭ 388 (+0%)
Mutual labels:  laravel
Laravel Eloquent Uuid
A simple drop-in solution for providing UUID support for the IDs of your Eloquent models.
Stars: ✭ 388 (+0%)
Mutual labels:  laravel

Set content security policy headers in a Laravel app

Latest Version on Packagist GitHub Workflow Status Check & fix styling Total Downloads

By default all scripts on a webpage are allowed to send and fetch 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). For a better idea of why you really need to set content security policy headers read this excellent blog post by David Gilbertson.

Setting Content Security Policy headers helps solve this problem. 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. We highly recommend that you read Mozilla's documentation on the Content Security Policy) before using this package.

If you're an audio visual learner you should check out this video on how to use this package. https://www.laraning.com/videos/spatie-csp-content-security-policy

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

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 policy will determine which CSP headers will be set. A valid CSP policy is
     * any class that extends `Spatie\Csp\Policies\Policy`
     */
    'policy' => Spatie\Csp\Policies\Basic::class,

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

    /*
     * All violations against the 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 policy.
     */
    '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 middleware 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 policy class as a parameter to the middleware:

// in a routes file
Route::get('my-page', 'MyController')->middleware(Spatie\Csp\AddCspHeaders::class . ':' . MyPolicy::class);

The given policy 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 policies. A CSP policy 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 or 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. Script/style hashes, as well, will be auto-detected and surrounded with quotes.

// in a policy
...
   ->addDirective(Directive::SCRIPT, Keyword::SELF) // will output `'self'` when outputting headers
   ->addDirective(Directive::STYLE, 'sha256-hash') // will output `'sha256-hash'` when outputting headers
...

You can add multiple policy options in the same directive giving an array as second parameter to addDirective or a single string in which every option is separated by one or more spaces.

// in a policy
...
   ->addDirective(Directive::SCRIPT, [
       Keyword::STRICT_DYNAMIC,
       Keyword::SELF,
       'www.google.com',
   ])
   ->addDirective(Directive::SCRIPT, 'strict-dynamic self  www.google.com')
   // will both output `'strict_dynamic' 'self' www.google.com` when outputting headers
...

There are also a few cases where you don't have to or don't need to specify a value, eg. upgrade-insecure-requests, block-all-mixed-content, ... In this case you can use the following value:

// in a policy
...
    ->addDirective(Directive::UPGRADE_INSECURE_REQUESTS, Value::NO_VALUE)
    ->addDirective(Directive::BLOCK_ALL_MIXED_CONTENT, Value::NO_VALUE);
...

This will output a CSP like this:

Content-Security-Policy: upgrade-insecure-requests;block-all-mixed-content

Creating policies

In the policy key of the csp config file is set to \Spatie\Csp\Policies\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\Policies;

use Spatie\Csp\Directive;
use Spatie\Csp\Value;

class Basic extends Policy
{
    public function configure()
    {
        $this
            ->addDirective(Directive::BASE, Keyword::SELF)
            ->addDirective(Directive::CONNECT, Keyword::SELF)
            ->addDirective(Directive::DEFAULT, Keyword::SELF)
            ->addDirective(Directive::FORM_ACTION, Keyword::SELF)
            ->addDirective(Directive::IMG, Keyword::SELF)
            ->addDirective(Directive::MEDIA, Keyword::SELF)
            ->addDirective(Directive::OBJECT, Keyword::NONE)
            ->addDirective(Directive::SCRIPT, Keyword::SELF)
            ->addDirective(Directive::STYLE, Keyword::SELF)
            ->addNonceForDirective(Directive::SCRIPT)
            ->addNonceForDirective(Directive::STYLE);
    }
}

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

namespace App\Services\Csp\Policies;

use Spatie\Csp\Directive;
use Spatie\Csp\Policies\Basic;

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

Don't forget to set the policy key in the csp config file to the class name of your policy (in this case it would be App\Services\Csp\Policies\MyCustomPolicy).

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 is 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 malicious scripts or styles.

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

// in a policy

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="{{ csp_nonce() }}">
   ...
</style>

<script nonce="{{ csp_nonce() }}">
   ...
</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 outright blocking all violations you can put a policy in report only mode. In this case all requests will be made, but all violations will display in your favourite browser's console.

To put a policy 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 multiple policies

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

Using whoops

Laravel comes with whoops, an error handling framework that helps you debug your application with a pretty visualization of exceptions. Whoops uses inline scripts and styles because it can't make any assumptions about the environment it is being used in, so it won't work unless you allow unsafe-inline for scripts and styles.

One approach to this problem is to check config('app.debug') when setting your policy. Unfortunately this bears the risk of forgetting to test your code with all CSP rules enabled and having your app break at deployment. Alternatively, you could allow unsafe-inline only on error pages by adding this to the render method of your exception handler (usually in app/Exceptions/Handler.php):

$this->container->singleton(AppPolicy::class, function ($app) {
    return new AppPolicy();
});
app(AppPolicy::class)->addDirective(Directive::SCRIPT, Keyword::UNSAFE_INLINE);
app(AppPolicy::class)->addDirective(Directive::STYLE, Keyword::UNSAFE_INLINE);

where AppPolicy is the name of your CSP policy. This also works in every other situation to change the policy at runtime, in which case the singleton registration should be done in a service provider instead of the exception handler.

Note that unsafe-inline only works if you're not also sending a nonce or a strict-dynamic directive, so to be able to use this workaround, you have to specify all your inline scripts' and styles' hashes in the CSP header.

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 [email protected] instead of using the issue tracker.

Credits

License

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].