diff --git a/app/Http/Controllers/SinglePageController.php b/app/Http/Controllers/SinglePageController.php index 3561f02e..deaf3dde 100644 --- a/app/Http/Controllers/SinglePageController.php +++ b/app/Http/Controllers/SinglePageController.php @@ -48,12 +48,9 @@ class SinglePageController extends Controller $installDocUrl = config('2fauth.installDocUrl'); $ssoDocUrl = config('2fauth.ssoDocUrl'); $exportSchemaUrl = config('2fauth.exportSchemaUrl'); - $cspNonce = Vite::cspNonce(); $isSecure = str_starts_with(config('app.url'), 'https'); - // if (Auth::user()->preferences) - - return view('landing')->with([ + $viewData = [ 'appSettings' => $settings, 'appConfig' => collect([ 'proxyAuth' => $proxyAuth, @@ -76,8 +73,13 @@ class SinglePageController extends Controller 'isTestingApp' => $isTestingApp, 'lang' => $lang, 'locales' => $locales, - 'cspNonce' => $cspNonce, 'isSecure' => $isSecure, - ]); + ]; + + if (config('2fauth.config.contentSecurityPolicy')) { + $viewData['cspNonce'] = Vite::cspNonce(); + } + + return view('landing')->with($viewData); } } diff --git a/app/Http/Middleware/AddContentSecurityPolicyHeaders.php b/app/Http/Middleware/AddContentSecurityPolicyHeaders.php index 80d00aee..2c8f2e26 100644 --- a/app/Http/Middleware/AddContentSecurityPolicyHeaders.php +++ b/app/Http/Middleware/AddContentSecurityPolicyHeaders.php @@ -17,18 +17,41 @@ class AddContentSecurityPolicyHeaders public function handle(Request $request, Closure $next) : Response { if (config('2fauth.config.contentSecurityPolicy')) { + // Some CSP directives can be used with nonce but not all of them. + // We build a space separated list of addresses to be allowed. Vite::useCspNonce(); + $authorizedAddresses[] = config('app.url') . ':*'; - $assetUrl = config('app.asset_url') != config('app.url') ? config('app.asset_url') : ''; + // We add custom asset url if defined + if (config('app.asset_url') && config('app.asset_url') != config('app.url')) { + $authorizedAddresses[] = config('app.asset_url') . ':*'; + } - $directives['script-src'] = "script-src 'nonce-" . Vite::cspNonce() . "' " . $assetUrl . ';'; - $directives['script-src-elem'] = "script-src-elem 'nonce-" . Vite::cspNonce() . "' " . $assetUrl . " 'strict-dynamic';"; - $directives['style-src'] = "style-src 'self' " . $assetUrl . " 'unsafe-inline';"; - $directives['connect-src'] = "connect-src 'self';"; - $directives['img-src'] = "img-src 'self' data: " . $assetUrl . ';'; - $directives['object-src'] = "object-src 'none';"; + // We add 'ws://' protocole and localhost ip address to avoid error with + // Vite hot reload (when running 'npm run dev') + // For the record: 127.0.0.1 is the only supported ip address as CSP + // is intended to work with domain names -it's a www security mecanism) + if (config('app.env') === 'development' && Vite::isRunningHot()) { + $authorizedAddresses[] = 'ws://' . $request->getHttpHost() . ':*'; + $authorizedAddresses[] = 'http://127.0.0.1:*'; + $authorizedAddresses[] = 'ws://127.0.0.1:*'; + } - $csp = implode(' ', $directives); + $authorizedAddresses = implode(' ', $authorizedAddresses); + + $directives['script-src'] = "script-src 'nonce-" . Vite::cspNonce() . "' 'strict-dynamic'"; + $directives['style-src'] = "style-src 'self' " . $authorizedAddresses . " 'unsafe-inline'"; + $directives['connect-src'] = "connect-src 'self' " . $authorizedAddresses; + $directives['img-src'] = "img-src 'self' " . $authorizedAddresses; + $directives['object-src'] = "object-src 'none'"; + $directives['default-src'] = "default-src 'self'"; + + // This one is to allow eval used by the vue devtools extension + if (config('app.env') === 'development') { + $directives['script-src'] .= " 'unsafe-eval'"; + } + + $csp = implode('; ', $directives); /** @disregard Undefined function */ /** @phpstan-ignore-next-line */ diff --git a/resources/views/landing.blade.php b/resources/views/landing.blade.php index f22b1bfe..b69fbf38 100644 --- a/resources/views/landing.blade.php +++ b/resources/views/landing.blade.php @@ -22,7 +22,8 @@
-