2020-06-22 22:31:01 -07:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
|
|
|
|
use Closure;
|
|
|
|
|
|
|
|
class SecurityHeaders
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Handle an incoming request.
|
|
|
|
*
|
|
|
|
* @param \Illuminate\Http\Request $request
|
|
|
|
* @param \Closure $next
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
|
2020-06-22 22:37:14 -07:00
|
|
|
// See https://securityheaders.com/
|
2020-06-22 22:31:01 -07:00
|
|
|
private $unwantedHeaderList = [
|
|
|
|
'X-Powered-By',
|
|
|
|
'Server',
|
|
|
|
];
|
|
|
|
|
|
|
|
public function handle($request, Closure $next)
|
|
|
|
{
|
|
|
|
$this->removeUnwantedHeaders($this->unwantedHeaderList);
|
|
|
|
$response = $next($request);
|
2020-06-23 00:26:09 -07:00
|
|
|
|
2020-06-22 22:31:01 -07:00
|
|
|
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
|
|
|
$response->headers->set('X-XSS-Protection', '1; mode=block');
|
2020-06-23 01:07:00 -07:00
|
|
|
|
|
|
|
// Ugh. Feature-Policy is dumb and clumsy and mostly irrelevant for Snipe-IT,
|
|
|
|
// since we don't provide any way to IFRAME anything in in the first place.
|
|
|
|
// There is currently no easy way to default ALL THE THINGS to 'none', but
|
|
|
|
// security audits will still ding you if you don't have this header, even
|
|
|
|
// though we don't allow IFRAMING in the first place.
|
|
|
|
//
|
|
|
|
// So for security and compliance sake, here we are. Sigh.
|
|
|
|
//
|
|
|
|
// See also:
|
|
|
|
// - https://developers.google.com/web/updates/2018/06/feature-policy
|
|
|
|
// - https://scotthelme.co.uk/a-new-security-header-feature-policy/
|
|
|
|
// - https://github.com/w3c/webappsec-feature-policy/issues/189
|
|
|
|
|
|
|
|
$feature_policy[] = "accelerometer 'none'";
|
|
|
|
$feature_policy[] = "ambient-light-sensor 'none'";
|
|
|
|
$feature_policy[] = "animations 'none'";
|
|
|
|
$feature_policy[] = "autoplay 'none'";
|
|
|
|
$feature_policy[] = "battery 'none'";
|
|
|
|
$feature_policy[] = "camera 'none'";
|
|
|
|
$feature_policy[] = "display-capture 'none'";
|
|
|
|
$feature_policy[] = "document-domain 'none'";
|
|
|
|
$feature_policy[] = "encrypted-media 'none'";
|
|
|
|
$feature_policy[] = "fullscreen 'none'";
|
|
|
|
$feature_policy[] = "geolocation 'none'";
|
|
|
|
$feature_policy[] = "gyroscope 'none'";
|
|
|
|
$feature_policy[] = "legacy-image-formats 'none'";
|
|
|
|
$feature_policy[] = "magnetometer 'none'";
|
|
|
|
$feature_policy[] = "microphone 'none'";
|
|
|
|
$feature_policy[] = "midi 'none'";
|
|
|
|
$feature_policy[] = "oversized-images 'none'";
|
|
|
|
$feature_policy[] = "payment 'none'";
|
|
|
|
$feature_policy[] = "picture-in-picture 'none'";
|
|
|
|
$feature_policy[] = "publickey-credentials 'none'";
|
|
|
|
$feature_policy[] = "sync-xhr 'none'";
|
|
|
|
$feature_policy[] = "unsized-media 'none'";
|
|
|
|
$feature_policy[] = "usb 'none'";
|
|
|
|
$feature_policy[] = "vibrate 'none'";
|
|
|
|
$feature_policy[] = "wake-lock 'none'";
|
|
|
|
$feature_policy[] = "xr-spatial-tracking 'none'";
|
|
|
|
|
|
|
|
$feature_policy = join(';', $feature_policy);
|
|
|
|
$response->headers->set('Feature-Policy', $feature_policy);
|
|
|
|
|
|
|
|
|
2020-06-22 22:31:01 -07:00
|
|
|
|
2020-06-23 00:26:09 -07:00
|
|
|
// 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.
|
|
|
|
|
2020-06-22 22:31:01 -07:00
|
|
|
if (config('app.allow_iframing') == false) {
|
|
|
|
$response->headers->set('X-Frame-Options', 'DENY');
|
|
|
|
}
|
|
|
|
|
2020-06-22 23:17:27 -07:00
|
|
|
|
2020-06-25 11:00:33 -07:00
|
|
|
// This defaults to false to maintain backwards compatibility for
|
2020-06-22 23:17:27 -07:00
|
|
|
// 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.
|
2020-06-23 00:26:09 -07:00
|
|
|
|
2020-06-22 23:17:27 -07:00
|
|
|
if ((config('app.debug')!='true') || (config('app.enable_csp')=='true')) {
|
2020-06-23 01:07:00 -07:00
|
|
|
$csp_policy[] = "default-src 'self'";
|
|
|
|
$csp_policy[] = "style-src 'self' 'unsafe-inline'";
|
2020-06-25 11:00:33 -07:00
|
|
|
$csp_policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval'";
|
2020-06-23 01:07:00 -07:00
|
|
|
$csp_policy[] = "connect-src 'self'";
|
|
|
|
$csp_policy[] = "object-src 'none'";
|
|
|
|
$csp_policy[] = "font-src 'self' data:";
|
2020-11-12 19:50:01 -08:00
|
|
|
$csp_policy[] = "img-src 'self' data: ".config('app.url')." ".env('PUBLIC_AWS_URL')." https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com";
|
2020-06-23 01:07:00 -07:00
|
|
|
$csp_policy = join(';', $csp_policy);
|
|
|
|
$response->headers->set('Content-Security-Policy', $csp_policy);
|
2020-06-22 23:17:27 -07:00
|
|
|
}
|
2020-06-22 22:31:01 -07:00
|
|
|
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function removeUnwantedHeaders($headerList)
|
|
|
|
{
|
|
|
|
foreach ($headerList as $header)
|
|
|
|
header_remove($header);
|
|
|
|
}
|
|
|
|
}
|