DEV Community

Cover image for Understanding and Implementing Content Security Policy (CSP) in Laravel
Nasrul Hazim Bin Mohamad
Nasrul Hazim Bin Mohamad

Posted on

Understanding and Implementing Content Security Policy (CSP) in Laravel

Content Security Policy (CSP) is a crucial security feature that helps protect your web applications from various attacks, particularly Cross-Site Scripting (XSS). In this guide, we'll dive deep into implementing CSP in Laravel applications, exploring both the basics and advanced techniques.

What is Content Security Policy?

Content Security Policy is an HTTP header that tells browsers which resources are allowed to load and execute in your web application. It's a powerful tool for preventing:

  • Cross-Site Scripting (XSS) attacks
  • Clickjacking
  • Malicious code injection
  • Unwanted resource loading

Common CSP Implementation Methods in Laravel

Method 1: Using .htaccess (Not Recommended)

While it's possible to implement CSP through .htaccess, this approach has several limitations:

Header set Content-Security-Policy "default-src 'self'; 
    script-src 'self' 'unsafe-eval' 'unsafe-inline';
    style-src 'self' 'unsafe-inline';"
Enter fullscreen mode Exit fullscreen mode

Drawbacks:

  • Server-specific (only works with Apache)
  • Harder to maintain
  • Can't dynamically adjust policies
  • No access to Laravel's features

Method 2: Laravel Middleware (Recommended)

The preferred approach is using Laravel middleware. Here's a step-by-step implementation:

  1. Create a dedicated middleware:
php artisan make:middleware ContentSecurityPolicy
Enter fullscreen mode Exit fullscreen mode
  1. Implement the middleware logic:
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ContentSecurityPolicy
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);

        $csp = $this->buildCspPolicy();

        $response->headers->set('Content-Security-Policy', $csp);

        return $response;
    }

    private function buildCspPolicy(): string
    {
        return "default-src 'self'; " .
               "img-src 'self' data: https://trusted-image-cdn.com; " .
               "style-src 'self' 'unsafe-inline' https://fonts.bunny.net; " .
               "font-src 'self' https://fonts.bunny.net; " .
               "script-src 'self' 'unsafe-eval' 'unsafe-inline'; " .
               "object-src 'none';";
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Register in app/Http/Kernel.php:
protected $middleware = [
    // ... other middleware
    \App\Http\Middleware\ContentSecurityPolicy::class,
];
Enter fullscreen mode Exit fullscreen mode

Advanced CSP Implementation

Environment-Specific Policies

Different environments often require different CSP configurations:

private function buildCspPolicy(): string
{
    if (app()->environment('production')) {
        return $this->getProductionPolicy();
    }

    return $this->getDevelopmentPolicy();
}

private function getProductionPolicy(): string
{
    return "default-src 'self'; " .
           "script-src 'self' 'nonce-" . $this->generateNonce() . "'; " .
           "style-src 'self';";
}

private function getDevelopmentPolicy(): string
{
    return "default-src 'self'; " .
           "script-src 'self' 'unsafe-eval' 'unsafe-inline'; " .
           "style-src 'self' 'unsafe-inline';";
}
Enter fullscreen mode Exit fullscreen mode

Using Nonces for Inline Scripts

Instead of using unsafe-inline, you can generate nonces for better security:

class ContentSecurityPolicy
{
    private function generateNonce(): string
    {
        $nonce = bin2hex(random_bytes(16));
        app()->singleton('csp-nonce', fn() => $nonce);
        return $nonce;
    }
}
Enter fullscreen mode Exit fullscreen mode

In your Blade views:

<script nonce="{{ app('csp-nonce') }}">
    // Your inline JavaScript
</script>
Enter fullscreen mode Exit fullscreen mode

CSP Reporting

Implement CSP reporting to monitor violations:

class ContentSecurityPolicy
{
    private function buildCspPolicy(): string
    {
        $policy = // ... your regular policy ...;

        if (app()->environment('production')) {
            $policy .= "report-uri /csp-report;";
        }

        return $policy;
    }
}
Enter fullscreen mode Exit fullscreen mode

Create a route to handle reports:

Route::post('csp-report', function (Request $request) {
    Log::channel('csp')->info('CSP Violation', $request->all());
});
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Start Strict: Begin with a strict policy and loosen only as needed:
"default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';"
Enter fullscreen mode Exit fullscreen mode
  1. Avoid Unsafe Directives: Minimize use of unsafe-inline and unsafe-eval:
// Instead of unsafe-inline, use nonces
"script-src 'self' 'nonce-{random-nonce}';"
Enter fullscreen mode Exit fullscreen mode
  1. Use Report-Only Mode: Test policies before enforcement:
$response->headers->set('Content-Security-Policy-Report-Only', $csp);
Enter fullscreen mode Exit fullscreen mode
  1. Regular Audits: Regularly review and update your CSP:
  2. Monitor CSP violation reports
  3. Remove unused directives
  4. Update policies when adding new resources

Troubleshooting Common Issues

1. Inline Scripts Not Working

Problem:

<script>
    alert('This won't work!');
</script>
Enter fullscreen mode Exit fullscreen mode

Solution:

<script nonce="{{ app('csp-nonce') }}">
    alert('This will work!');
</script>
Enter fullscreen mode Exit fullscreen mode

2. Third-Party Resources Blocked

Problem:

"default-src 'self';" // Blocks external resources
Enter fullscreen mode Exit fullscreen mode

Solution:

"default-src 'self'; script-src 'self' https://trusted-cdn.com;"
Enter fullscreen mode Exit fullscreen mode

Laravel Telescope & Laravel Horizon

For Laravel Telescope & Laravel Horizon, I've added specifically for them in the middleware:

// Special CSP for Horizon
if ($request->is('horizon*')) {
    $csp = "default-src 'self'; ".
           "img-src 'self' data: https://ui-avatars.com; ".
           "style-src 'self' 'unsafe-inline' https://fonts.bunny.net; ".
           "font-src 'self' https://fonts.bunny.net; ".
           "script-src 'self' 'unsafe-inline'; ".
           "object-src 'none';";
}

// Special CSP for Telescope
if ($request->is('telescope*')) {
    $csp = "default-src 'self'; ".
           "img-src 'self' data: https://ui-avatars.com; ".
           "style-src 'self' 'unsafe-inline' https://fonts.bunny.net; ".
           "font-src 'self' https://fonts.bunny.net; ".
           "script-src 'self' 'unsafe-inline'; ".
           "object-src 'none';";
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Implementing CSP in Laravel through middleware provides a robust, maintainable solution for securing your web application. While the initial setup might require some effort, the security benefits far outweigh the implementation costs. Remember to:

  • Use middleware instead of .htaccess
  • Implement environment-specific policies
  • Use nonces or hashes instead of unsafe-inline
  • Monitor CSP violations
  • Regularly audit and update your policies

By following these guidelines, you'll significantly enhance your application's security against XSS and other common web vulnerabilities.

Resources


Photo by Willian Justen de Vasconcellos on Unsplash

Top comments (1)

Collapse
 
nuzulfikrie profile image
nuzulfikrie

good info and informative steps