Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Horizon doesn't work when using content security policy headers #576

Closed
tomgrohl opened this issue Apr 17, 2019 · 6 comments
Closed

Horizon doesn't work when using content security policy headers #576

tomgrohl opened this issue Apr 17, 2019 · 6 comments

Comments

@tomgrohl
Copy link

  • Horizon Version: 3.1.1
  • Laravel Version: 5.8.11
  • PHP Version: 7.2
  • Redis Driver & Version: predis 1.1.1
  • Database Driver & Version: Mysql 5.7.25

Description:

Horizon doesn't work when using content security policy headers. Because of the changes to the view files between versions 1 and 3. i.e going from an empty app:

<div id="root"></div>

to a div with html elements and a Vue component inside it,

<div id="horizon" v-cloak>
...
</div>

This causes Vue to use eval which is unsafe and not allow by CSP.

Steps To Reproduce:

  1. Add a middleware for content security policy and register in app.php:
<?php

namespace App\Http\Middleware;

use Closure;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class ContentSecurityPolicy
 *
 * @package App\Http\Middleware
 */
class ContentSecurityPolicy
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $policy = [
            'default-src' => [
            ],
            'script-src' => [
                '\'unsafe-inline\'',
                'data:',
                '*.googleapis.com',
                '*.twitter.com',
                '*.facebook.net',
                'www.googleadservices.com',
                'www.gstatic.com',
                'www.google.com',
                'google.com',
                'google.co.uk',
                'use.fontawesome.com',
            ],
            'style-src' => [
                '\'unsafe-inline\'',
                'fonts.googleapis.com',
                'tagmanager.google.com',
                'maxcdn.bootstrapcdn.com',
                'cdn-images.mailchimp.com',
                'use.fontawesome.com',
            ],
            'img-src' => [
                '*',
                'data:',
            ],
            'font-src' => [
                'data:',
                'fonts.googleapis.com',
                'fonts.gstatic.com',
                'use.fontawesome.com',
            ],
            'connect-src' => [
                'fonts.googleapis.com',
            ],
            'frame-src' => [
                'www.google.com',
            ],
        ];


        /** @var Response $response */
        $response = $next($request);

        if ('local' === app()->environment()) {
            $policy['script-src'][] = '\'unsafe-eval\'';
        }
        
        array_walk($policy, function(&$item, $key) {
            $item = $key . ' \'self\' ' . implode(' ', $item) . ';';
        });

        if ($response instanceof Response) {
            $response->headers->set('Content-Security-Policy', implode('', array_values($policy)));
        }

        return $response;
    }
}
  1. Try and access the Horizon dashboard. You should get a white screen. i.e horizon not loading.

The only workaround I'be found for now is by replacing the line:

if ('local' === app()->environment()) {
    $policy['script-src'][] = '\'unsafe-eval\'';
}

with:

if ('local' === app()->environment() || $request->routeIs('horizon.*')) {
    $policy['script-src'][] = '\'unsafe-eval\'';
}

But I'm not sure if this is ideal.

@driesvints
Copy link
Member

I'm not really sure if it's necessary to support this tbh. This is just the way it works now and I don't know too much of view myself to know if this would at all be possible.

@themsaid
Copy link
Member

Horizon is meant to be accessible only by app developers, we don’t really consider it to be accessible to the public and thus these extra checks weren’t in mind while working on it. You need to ignore horizon routes as shared in your example.

@tomgrohl
Copy link
Author

@driesvints a fix would be to move the contents inside the horizon div to a Vue component i.e App, much like Horizon did in version 1.0+.

What @themsaid makes sense though. We have access controlled by a staff permission so its only accessible by developers.

I opened this in case this wasn't meant to be an issue.

@driesvints
Copy link
Member

Yeah, the horizon dashboard should only be accessible by your dev team.

@francoism90
Copy link

francoism90 commented Jul 12, 2020

I'm having the same issue using nginx secure headers:

# security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;

They are recommend by https://nginxconfig.io/

Any solutions? Thanks.

Edit: it seems this is only in Firefox.

@ultrono
Copy link

ultrono commented Sep 2, 2023

As this page is ranked highly in Google there is a workaround for this if using the excellent https://github.com/spatie/laravel-csp.

In the shouldBeApplied() method simply add a check to determine if you're viewing the Horizon dashboard (which should be locked down to auth'd users only):

if (request()->segment(index: 1) === config(key: 'horizon.path')) {
   return false;
}

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants