From e359ac4eeace6536cc6033e4386267e6eee0a8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Mon, 23 May 2022 15:02:54 +0200 Subject: [PATCH 1/7] Add CSP report URI --- app/Providers/RouteServiceProvider.php | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 9f141d0..be41a7d 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -35,6 +35,8 @@ class RouteServiceProvider extends ServiceProvider { $this->configureRateLimiting(); + $this->cspReportRoute(); + $this->routes(function () { Route::prefix('api') ->middleware('api') @@ -70,4 +72,42 @@ class RouteServiceProvider extends ServiceProvider return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); } + + /** + * * Register CSP report URI + * + * Put this function inside RouteServiceProvider, + * and call it in RouteServiceProvider::boot + * + * ! Use this in conjonction with kwaadpepper/exception-handler + * + * @return void + */ + private function cspReportRoute() + { + // * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware + \Illuminate\Support\Facades\Route::post('/csp-report', function () { + $isCspRequest = request()->header('Content-Type') === 'application/csp-report'; + if (!$isCspRequest or !request()->json('csp-report') or !is_array(request()->json('csp-report'))) { + abort(401); + } + // * Flatten if needed + $cspReport = collect(request()->json('csp-report')) + ->flatMap(function ($v, $k) { + return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v]; + })->all(); + // * Filter array keys + $cspReport = collect(array_intersect_key($cspReport, [ + 'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '', + 'referrer' => '', 'violated-directive' => '' + ])); + report(new \Illuminate\Http\Client\HttpClientException(sprintf( + "CSP REPORT for %s : \n %s", + $cspReport->get('document-uri'), + $cspReport->implode("\n") + ))); + })->middleware('throttle:5,2') + ->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class) + ->name('cspReportUri'); + } } From e7f876018eb822ebf5aee3fec845d45b85118b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Mon, 23 May 2022 15:12:11 +0200 Subject: [PATCH 2/7] Rework App Http Kernel + Add Secure Headers --- app/Http/Kernel.php | 4 +- app/Http/Middleware/Authenticate.php | 3 +- .../PreventRequestsDuringMaintenance.php | 3 +- app/Http/Middleware/SecureHeaders.php | 71 +++++++++++++++++++ app/Http/Middleware/StartSession.php | 3 +- app/Http/Middleware/TrimStrings.php | 2 +- app/Http/Middleware/TrustHosts.php | 2 +- app/Http/Middleware/TrustProxies.php | 7 +- app/Http/Middleware/VerifyCsrfToken.php | 5 +- resources/views/files/create.blade.php | 2 +- resources/views/layout/app.blade.php | 2 +- resources/views/macroplanning/view.blade.php | 2 +- resources/views/schedule/index.blade.php | 2 +- 13 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 app/Http/Middleware/SecureHeaders.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index e4d9604..3083df5 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -14,7 +14,7 @@ class Kernel extends HttpKernel * @var array */ protected $middleware = [ - \App\Http\Middleware\StartSession::class, + \App\Http\Middleware\TrustHosts::class, \App\Http\Middleware\TrustProxies::class, \Fruitcake\Cors\HandleCors::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class, @@ -32,9 +32,11 @@ class Kernel extends HttpKernel 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \App\Http\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + \App\Http\Middleware\SecureHeaders::class ], 'api' => [ diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index aeb9d80..7e84ea7 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -9,11 +9,12 @@ class Authenticate extends Middleware /** * Get the path the user should be redirected to when they are not authenticated. * - * @param \Illuminate\Http\Request $request + * @param mixed $request * @return string|null */ protected function redirectTo($request) { + /** @var \Illuminate\Http\Request $request */ if (! $request->expectsJson()) { return route('login'); } diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php index 402ea32..467b7e2 100644 --- a/app/Http/Middleware/PreventRequestsDuringMaintenance.php +++ b/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -11,6 +11,5 @@ class PreventRequestsDuringMaintenance extends Middleware * * @var array */ - protected $except = [ - ]; + protected $except = []; } diff --git a/app/Http/Middleware/SecureHeaders.php b/app/Http/Middleware/SecureHeaders.php new file mode 100644 index 0000000..89ee7a2 --- /dev/null +++ b/app/Http/Middleware/SecureHeaders.php @@ -0,0 +1,71 @@ +removeUnwantedHeaders($this->unwantedHeaderList); + $response = $next($request); + $response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade'); + $response->headers->set('X-Content-Type-Options', 'nosniff'); + $response->headers->set('X-XSS-Protection', '1; mode=block'); + $response->headers->set('X-Frame-Options', 'SAMEORIGIN'); + $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;'); + $cspPolicies = [ + // phpcs:ignore Generic.Files.LineLength.TooLong + "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://use.fontawesome.com;", + // phpcs:ignore Generic.Files.LineLength.TooLong + "script-src 'self' $jsDev https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://bootswatch.com;" + ]; + $response->headers->set( + 'Content-Security-Policy', + implode(' ', $cspPolicies) . $reportUri + ); + $response->headers->set('Feature-Policy', 'fullscreen \'self\';'); + return $response; + } + + /** + * Remove unwanted headers + * + * @param array $headerList + * @return void + */ + private function removeUnwantedHeaders(array $headerList) + { + foreach ($headerList as $header) { + header_remove($header); + } + } +} diff --git a/app/Http/Middleware/StartSession.php b/app/Http/Middleware/StartSession.php index 6c8563e..c8e1451 100644 --- a/app/Http/Middleware/StartSession.php +++ b/app/Http/Middleware/StartSession.php @@ -9,11 +9,12 @@ class StartSession extends MiddlewareStartSession /** * Save the session data to storage. * - * @param \Illuminate\Http\Request $request + * @param mixed $request * @return void */ protected function saveSession($request) { + /** @var \Illuminate\Http\Request $request */ if (!$request->is('/ping')) { parent::saveSession($request); } diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php index 5a50e7b..e763dfb 100644 --- a/app/Http/Middleware/TrimStrings.php +++ b/app/Http/Middleware/TrimStrings.php @@ -9,7 +9,7 @@ class TrimStrings extends Middleware /** * The names of the attributes that should not be trimmed. * - * @var array + * @var array */ protected $except = [ 'password', diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php index b0550cf..7186414 100644 --- a/app/Http/Middleware/TrustHosts.php +++ b/app/Http/Middleware/TrustHosts.php @@ -9,7 +9,7 @@ class TrustHosts extends Middleware /** * Get the host patterns that should be trusted. * - * @return array + * @return array */ public function hosts() { diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 03b7890..059153b 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -19,5 +19,10 @@ class TrustProxies extends Middleware * * @var integer */ - protected $headers = Request::HEADER_X_FORWARDED_ALL; + protected $headers = + Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB; } diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index ed591f2..70e23e1 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -9,8 +9,7 @@ class VerifyCsrfToken extends Middleware /** * The URIs that should be excluded from CSRF verification. * - * @var array + * @var array */ - protected $except = [ - ]; + protected $except = []; } diff --git a/resources/views/files/create.blade.php b/resources/views/files/create.blade.php index 53ae673..beb5215 100644 --- a/resources/views/files/create.blade.php +++ b/resources/views/files/create.blade.php @@ -45,7 +45,7 @@ @endsection @push('scripts') - + @endauth diff --git a/resources/views/macroplanning/view.blade.php b/resources/views/macroplanning/view.blade.php index 0ccf393..9ace2e0 100644 --- a/resources/views/macroplanning/view.blade.php +++ b/resources/views/macroplanning/view.blade.php @@ -18,7 +18,7 @@ @endsection @push('scriptsConstants') - -@endpush diff --git a/resources/views/layout/app.blade.php b/resources/views/layout/app.blade.php index 3ea579d..31be5ac 100644 --- a/resources/views/layout/app.blade.php +++ b/resources/views/layout/app.blade.php @@ -17,12 +17,9 @@ - - - @crudPoliciesCSS @stack('customCss') @@ -42,8 +39,6 @@ @include('layout.footer') - - @stack('scriptsConstants') @stack('scripts') diff --git a/resources/views/layout/footer.blade.php b/resources/views/layout/footer.blade.php index 3eb81af..c8cd2ff 100644 --- a/resources/views/layout/footer.blade.php +++ b/resources/views/layout/footer.blade.php @@ -1,6 +1,6 @@