From 36c8f7f4f116666c63ae7bc0d12e15f77a8fd6bc Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 22 Jun 2020 22:31:01 -0700 Subject: [PATCH 1/6] Additional security headers --- app/Http/Kernel.php | 7 +-- app/Http/Middleware/FrameGuard.php | 24 ---------- app/Http/Middleware/NosniffGuard.php | 21 --------- app/Http/Middleware/SecurityHeaders.php | 56 ++++++++++++++++++++++++ app/Http/Middleware/XssProtectHeader.php | 22 ---------- 5 files changed, 58 insertions(+), 72 deletions(-) delete mode 100644 app/Http/Middleware/FrameGuard.php delete mode 100644 app/Http/Middleware/NosniffGuard.php create mode 100644 app/Http/Middleware/SecurityHeaders.php delete mode 100644 app/Http/Middleware/XssProtectHeader.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index da3c5092b910..004549679159 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -17,15 +17,12 @@ class Kernel extends HttpKernel \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \App\Http\Middleware\FrameGuard::class, - \App\Http\Middleware\XssProtectHeader::class, - \App\Http\Middleware\ReferrerPolicyHeader::class, - \App\Http\Middleware\ContentSecurityPolicyHeader::class, - \App\Http\Middleware\NosniffGuard::class, \Fideloper\Proxy\TrustProxies::class, \App\Http\Middleware\CheckForSetup::class, \App\Http\Middleware\CheckForDebug::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + \App\Http\Middleware\SecurityHeaders::class, + ]; /** diff --git a/app/Http/Middleware/FrameGuard.php b/app/Http/Middleware/FrameGuard.php deleted file mode 100644 index beb19f20f128..000000000000 --- a/app/Http/Middleware/FrameGuard.php +++ /dev/null @@ -1,24 +0,0 @@ -headers->set('X-Frame-Options', 'SAMEORIGIN', false); - } - return $response; - - } -} diff --git a/app/Http/Middleware/NosniffGuard.php b/app/Http/Middleware/NosniffGuard.php deleted file mode 100644 index 295f5e75afb1..000000000000 --- a/app/Http/Middleware/NosniffGuard.php +++ /dev/null @@ -1,21 +0,0 @@ -headers->set('X-Content-Type-Options', 'nosniff', false); - return $response; - } -} diff --git a/app/Http/Middleware/SecurityHeaders.php b/app/Http/Middleware/SecurityHeaders.php new file mode 100644 index 000000000000..8e0b5b945c45 --- /dev/null +++ b/app/Http/Middleware/SecurityHeaders.php @@ -0,0 +1,56 @@ +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('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); + + if (config('app.allow_iframing') == false) { + $response->headers->set('X-Frame-Options', 'DENY'); + } + + $policy[] = "default-src 'self'"; + $policy[] = "style-src 'self' 'unsafe-inline' oss.maxcdn.com"; + $policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval' cdnjs.cloudflare.com"; + $policy[] = "connect-src 'self'"; + $policy[] = "object-src 'none'"; + $policy[] = "font-src 'self' data:"; + $policy[] = "img-src 'self' data: gravatar.com"; + $policy = join(';', $policy); + $response->headers->set('Content-Security-Policy', $policy); + + return $response; + } + + private function removeUnwantedHeaders($headerList) + { + foreach ($headerList as $header) + header_remove($header); + } +} diff --git a/app/Http/Middleware/XssProtectHeader.php b/app/Http/Middleware/XssProtectHeader.php deleted file mode 100644 index 868d100f3788..000000000000 --- a/app/Http/Middleware/XssProtectHeader.php +++ /dev/null @@ -1,22 +0,0 @@ -headers->set('X-XSS-Protection', $mode); - return $response; - } -} From a716382ac43d0a58b96604a3ec15e389b7ae97c2 Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 22 Jun 2020 22:33:37 -0700 Subject: [PATCH 2/6] =?UTF-8?q?Removed=20CSP=20middleware=20(it=E2=80=99s?= =?UTF-8?q?=20added=20in=20the=20general=20header)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContentSecurityPolicyHeader.php | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 app/Http/Middleware/ContentSecurityPolicyHeader.php diff --git a/app/Http/Middleware/ContentSecurityPolicyHeader.php b/app/Http/Middleware/ContentSecurityPolicyHeader.php deleted file mode 100644 index 45c87a59e3fb..000000000000 --- a/app/Http/Middleware/ContentSecurityPolicyHeader.php +++ /dev/null @@ -1,35 +0,0 @@ -headers->set('Content-Security-Policy', $policy); - return $response; - } -} From 43042ad8412d8d89a9b09e47e5da8b276c9655f2 Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 22 Jun 2020 22:35:59 -0700 Subject: [PATCH 3/6] Consolidated ReferrerPolicy into new SecurityHeaders file --- app/Http/Middleware/ReferrerPolicyHeader.php | 21 -------------------- app/Http/Middleware/SecurityHeaders.php | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 app/Http/Middleware/ReferrerPolicyHeader.php diff --git a/app/Http/Middleware/ReferrerPolicyHeader.php b/app/Http/Middleware/ReferrerPolicyHeader.php deleted file mode 100644 index 430ce45af38c..000000000000 --- a/app/Http/Middleware/ReferrerPolicyHeader.php +++ /dev/null @@ -1,21 +0,0 @@ -headers->set('Referrer-Policy', config('app.referrer_policy')); - return $response; - } -} diff --git a/app/Http/Middleware/SecurityHeaders.php b/app/Http/Middleware/SecurityHeaders.php index 8e0b5b945c45..5a9b3ae6f235 100644 --- a/app/Http/Middleware/SecurityHeaders.php +++ b/app/Http/Middleware/SecurityHeaders.php @@ -26,7 +26,7 @@ public function handle($request, Closure $next) { $this->removeUnwantedHeaders($this->unwantedHeaderList); $response = $next($request); - $response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade'); + $response->headers->set('Referrer-Policy', config('app.referrer_policy')); $response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('X-XSS-Protection', '1; mode=block'); $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); From 4fb880384fd455bd59a3b91c4244c392d7198c48 Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 22 Jun 2020 22:37:14 -0700 Subject: [PATCH 4/6] Changed comment --- app/Http/Middleware/SecurityHeaders.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Http/Middleware/SecurityHeaders.php b/app/Http/Middleware/SecurityHeaders.php index 5a9b3ae6f235..7a75bfdc9dc3 100644 --- a/app/Http/Middleware/SecurityHeaders.php +++ b/app/Http/Middleware/SecurityHeaders.php @@ -14,9 +14,7 @@ class SecurityHeaders * @return mixed */ - // Enumerate headers which you do not want in your application's responses. - // Great starting point would be to go check out @Scott_Helme's: - // https://securityheaders.com/ + // See https://securityheaders.com/ private $unwantedHeaderList = [ 'X-Powered-By', 'Server', From 05b3a9ad7e72cc71b09ed8ef2e87db19fa3700ee Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 22 Jun 2020 23:17:27 -0700 Subject: [PATCH 5/6] Config variable for HSTS --- .env.example | 1 + app/Http/Middleware/SecurityHeaders.php | 35 ++++++++++++++++++------- config/app.php | 26 +++++++++++++----- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index b3b0b0e4ed6d..4ec2ee6aab80 100644 --- a/.env.example +++ b/.env.example @@ -71,6 +71,7 @@ ALLOW_IFRAMING=false REFERRER_POLICY=same-origin ENABLE_CSP=false CORS_ALLOWED_ORIGINS=null +ENABLE_HSTS=false # -------------------------------------------- # OPTIONAL: CACHE SETTINGS diff --git a/app/Http/Middleware/SecurityHeaders.php b/app/Http/Middleware/SecurityHeaders.php index 7a75bfdc9dc3..2c35ad80eb09 100644 --- a/app/Http/Middleware/SecurityHeaders.php +++ b/app/Http/Middleware/SecurityHeaders.php @@ -24,24 +24,39 @@ public function handle($request, Closure $next) { $this->removeUnwantedHeaders($this->unwantedHeaderList); $response = $next($request); + $response->headers->set('Referrer-Policy', config('app.referrer_policy')); $response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('X-XSS-Protection', '1; mode=block'); - $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); + $response->headers->set('Feature-Policy', 'self'); if (config('app.allow_iframing') == false) { $response->headers->set('X-Frame-Options', 'DENY'); } - $policy[] = "default-src 'self'"; - $policy[] = "style-src 'self' 'unsafe-inline' oss.maxcdn.com"; - $policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval' cdnjs.cloudflare.com"; - $policy[] = "connect-src 'self'"; - $policy[] = "object-src 'none'"; - $policy[] = "font-src 'self' data:"; - $policy[] = "img-src 'self' data: gravatar.com"; - $policy = join(';', $policy); - $response->headers->set('Content-Security-Policy', $policy); + + // This defaults to false to maintain backwards compatibility + // people who are not running Snipe-IT over TLS (shame, shame, shame!) + // Seriously though, please run Snipe-IT over TLS. Let's Encrypt is free. + // https://letsencrypt.org + + if (config('app.enable_hsts') === true) { + $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); + } + + // We have to exclude debug mode here because debugbar pulls from a CDN or two + // and it will break things. + if ((config('app.debug')!='true') || (config('app.enable_csp')=='true')) { + $policy[] = "default-src 'self'"; + $policy[] = "style-src 'self' 'unsafe-inline'"; + $policy[] = "script-src 'self' 'unsafe-inline'"; + $policy[] = "connect-src 'self'"; + $policy[] = "object-src 'none'"; + $policy[] = "font-src 'self' data:"; + $policy[] = "img-src 'self' data: gravatar.com"; + $policy = join(';', $policy); + $response->headers->set('Content-Security-Policy', $policy); + } return $response; } diff --git a/config/app.php b/config/app.php index 07d2ac6ef1e8..42044284a3c3 100755 --- a/config/app.php +++ b/config/app.php @@ -196,19 +196,33 @@ 'private_uploads' => storage_path().'/private_uploads', + /* + |-------------------------------------------------------------------------- + | ALLOW I-FRAMING + |-------------------------------------------------------------------------- + | + | Normal users will never need to edit this. This option lets you run + | Snipe-IT within an I-Frame, which is normally disabled by default for + | security reasons, to prevent clickjacking. It should normally be set to false. + | + */ + + 'allow_iframing' => env('ALLOW_IFRAMING', false), + + /* |-------------------------------------------------------------------------- - | ALLOW I-FRAMING + | ENABLE HTTP Strict Transport Security (HSTS) |-------------------------------------------------------------------------- | - | Normal users will never need to edit this. This option lets you run - | Snipe-IT within an I-Frame, which is normally disabled by default for - | security reasons, to prevent clickjacking. It should normally be set to false. + | This is set to default false for backwards compatibilty but should be + | set to true if the hosting environment allows it. + | + | See https://scotthelme.co.uk/hsts-the-missing-link-in-tls/ | */ - 'allow_iframing' => env('ALLOW_IFRAMING', false), - + 'enable_hsts' => env('ENABLE_HSTS', false), /* |-------------------------------------------------------------------------- From 00b051b8c7f1af5218a11f2b33fcab37934bd894 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 23 Jun 2020 00:26:09 -0700 Subject: [PATCH 6/6] Added a few more comments --- app/Http/Middleware/SecurityHeaders.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Http/Middleware/SecurityHeaders.php b/app/Http/Middleware/SecurityHeaders.php index 2c35ad80eb09..b50df5716c0c 100644 --- a/app/Http/Middleware/SecurityHeaders.php +++ b/app/Http/Middleware/SecurityHeaders.php @@ -24,12 +24,20 @@ public function handle($request, Closure $next) { $this->removeUnwantedHeaders($this->unwantedHeaderList); $response = $next($request); - - $response->headers->set('Referrer-Policy', config('app.referrer_policy')); + $response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('X-XSS-Protection', '1; mode=block'); $response->headers->set('Feature-Policy', 'self'); + // Defaults to same-origin if REFERRER_POLICY is not set in the .env + $response->headers->set('Referrer-Policy', config('app.referrer_policy')); + + // The .env var ALLOW_IFRAMING defaults to false (which disallows IFRAMING) + // if not present, but some unique cases require this to be enabled. + // For example, some IT depts have IFRAMED Snipe-IT into their IT portal + // for convenience so while it is normally disallowed, there is + // an override that exists. + if (config('app.allow_iframing') == false) { $response->headers->set('X-Frame-Options', 'DENY'); } @@ -46,6 +54,7 @@ public function handle($request, Closure $next) // We have to exclude debug mode here because debugbar pulls from a CDN or two // and it will break things. + if ((config('app.debug')!='true') || (config('app.enable_csp')=='true')) { $policy[] = "default-src 'self'"; $policy[] = "style-src 'self' 'unsafe-inline'";