Rework App Http Kernel + Add Secure Headers

This commit is contained in:
Jérémy Munsch 2022-05-23 15:12:11 +02:00
parent e359ac4eea
commit e7f876018e
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
13 changed files with 93 additions and 15 deletions

View file

@ -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' => [

View file

@ -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');
}

View file

@ -11,6 +11,5 @@ class PreventRequestsDuringMaintenance extends Middleware
*
* @var array
*/
protected $except = [
];
protected $except = [];
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
class SecureHeaders
{
/**
* 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/
*
* @var array
*/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
View::share('nonce', $nonce = \uniqid());
// * For VueJS dev tool to execute
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-$nonce'";
$reportUri = Route::has('cspReportUri') ?
\sprintf('report-uri %s', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
$this->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);
}
}
}

View file

@ -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);
}

View file

@ -9,7 +9,7 @@ class TrimStrings extends Middleware
/**
* The names of the attributes that should not be trimmed.
*
* @var array
* @var array<int, string>
*/
protected $except = [
'password',

View file

@ -9,7 +9,7 @@ class TrustHosts extends Middleware
/**
* Get the host patterns that should be trusted.
*
* @return array
* @return array<int, string|null>
*/
public function hosts()
{

View file

@ -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;
}

View file

@ -9,8 +9,7 @@ class VerifyCsrfToken extends Middleware
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
* @var array<int, string>
*/
protected $except = [
];
protected $except = [];
}

View file

@ -45,7 +45,7 @@
@endsection
@push('scripts')
<script>
<script nonce="{{ $nonce }}">
$(document).ready(() => {
let changeFields = () => {
let $iUri = $('#uri');

View file

@ -50,7 +50,7 @@
@crudPoliciesJS()
@auth
{{-- JS SESSION ALERT IF SESSION TIMEOUT --}}
<script>var __SESSION_WATCH = true;</script>
<script nonce="{{ $nonce }}">var __SESSION_WATCH = true;</script>
@endauth
</body>
</html>

View file

@ -18,7 +18,7 @@
@endsection
@push('scriptsConstants')
<script>
<script nonce="{{ $nonce }}">
window._routes = {
"macroplanning.activity": "{{ route('macroplanning.activity') }}",
"projects.show": "{{ route('projects.show', ['project' => 'PROJECT']) }}",

View file

@ -30,7 +30,7 @@
@endsection
@push('scriptsConstants')
<script>
<script nonce="{{ $nonce }}">
window._routes = {
"schedule.index": "{{ route('schedule.index') }}",
"ticket.show": "{{ route('ticket.show', 'TICKET') }}",