mirror of
https://github.com/typemill/typemill.git
synced 2025-07-31 11:20:15 +02:00
Version 2.1.0 CSP Middleware
This commit is contained in:
14
system/typemill/Events/OnCspLoaded.php
Normal file
14
system/typemill/Events/OnCspLoaded.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Events;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event for the page rendering data.
|
||||
*/
|
||||
|
||||
class OnCspLoaded extends BaseEvent
|
||||
{
|
||||
|
||||
}
|
@@ -11,28 +11,27 @@ class CspHeadersMiddleware implements MiddlewareInterface
|
||||
{
|
||||
protected $settings;
|
||||
|
||||
protected $urlinfo;
|
||||
|
||||
public function __construct($settings, $urlinfo)
|
||||
protected $cspFromPlugins = false;
|
||||
|
||||
protected $cspFromTheme = false;
|
||||
|
||||
public function __construct($settings, $cspFromPlugins, $cspFromTheme)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
|
||||
$this->urlinfo = $urlinfo;
|
||||
$this->$cspFromPlugins = $cspFromPlugins;
|
||||
|
||||
$this->$cspFromTheme = $cspFromTheme;
|
||||
}
|
||||
|
||||
public function process(Request $request, RequestHandler $handler) :response
|
||||
{
|
||||
$scheme = $request->getUri()->getScheme();
|
||||
|
||||
{
|
||||
# add the custom headers to the response after everything is processed
|
||||
$response = $handler->handle($request);
|
||||
|
||||
###################
|
||||
# CSP HEADER #
|
||||
###################
|
||||
$whitelist = ["'unsafe-inline'", "'unsafe-eval'", "'self'", "*.youtube-nocookie.com", "*.youtube.com"];
|
||||
|
||||
$cspdomains = isset($this->settings['cspdomains']) ? trim($this->settings['cspdomains']) : false;
|
||||
$defaultsrc = "default-src 'unsafe-inline' 'unsafe-eval' 'self'";
|
||||
|
||||
if($cspdomains && $cspdomains != '')
|
||||
{
|
||||
@@ -42,23 +41,29 @@ class CspHeadersMiddleware implements MiddlewareInterface
|
||||
$cspdomain = trim($cspdomain);
|
||||
if($cspdomain != '')
|
||||
{
|
||||
$defaultsrc .= ' ' . $cspdomain;
|
||||
$whitelist[] = $cspdomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# dispatch to get from plugins
|
||||
# get yaml from current theme
|
||||
# add csp from plugins
|
||||
if($this->cspFromPlugins && is_array($this->cspFromPlugins) && !empty($this->cspFromPlugins))
|
||||
{
|
||||
$whitelist = array_merge($whitelist, $this->cspFromPlugins);
|
||||
}
|
||||
|
||||
# add csp from current theme
|
||||
if($this->cspFromTheme && is_array($this->cspFromTheme) && !empty($this->cspFromTheme))
|
||||
{
|
||||
$whitelist = array_merge($whitelist, $this->cspFromTheme);
|
||||
}
|
||||
|
||||
$whitelist = array_unique($whitelist);
|
||||
$whitelist = implode(' ', $whitelist);
|
||||
|
||||
# Define the Content Security Policy header
|
||||
$cspHeader = $defaultsrc . ";"; // Default source is restricted to 'self'
|
||||
# $cspHeader .= "frame-src 'self' https://www.youtube.com;"; // Allowing embedding YouTube videos
|
||||
# $cspHeader .= "img-src *";
|
||||
# $cspHeader .= "media-src *";
|
||||
# $cspHeader .= "script-src *";
|
||||
# $cspHeader .= "style-src *";
|
||||
# $cspHeader .= "object-src 'none'";
|
||||
|
||||
$cspHeader = "default-src " . $whitelist . ";";
|
||||
|
||||
# Add the Content Security Policy header to the response
|
||||
$response = $response->withHeader('Content-Security-Policy', $cspHeader);
|
||||
|
||||
|
@@ -92,7 +92,7 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
if(isset($routes['api']) && !empty($routes['api']))
|
||||
{
|
||||
foreach($routes['api'] as $pluginRoute)
|
||||
{
|
||||
{
|
||||
$method = $pluginRoute['httpMethod'] ?? false;
|
||||
$route = $pluginRoute['route'] ?? false;
|
||||
$class = $pluginRoute['class'] ?? false;
|
||||
|
@@ -4,6 +4,7 @@ use Slim\Routing\RouteCollectorProxy;
|
||||
use Typemill\Middleware\WebRedirectIfAuthenticated;
|
||||
use Typemill\Middleware\WebRedirectIfUnauthenticated;
|
||||
use Typemill\Middleware\WebAuthorization;
|
||||
use Typemill\Middleware\CspHeadersMiddleware;
|
||||
use Typemill\Controllers\ControllerWebSetup;
|
||||
use Typemill\Controllers\ControllerWebAuth;
|
||||
use Typemill\Controllers\ControllerWebRecover;
|
||||
@@ -31,7 +32,7 @@ $app->group('/tm', function (RouteCollectorProxy $group) use ($settings) {
|
||||
$group->post('/reset', ControllerWebRecover::class . ':resetPassword')->setName('auth.reset');
|
||||
}
|
||||
|
||||
})->add(new WebRedirectIfAuthenticated($routeParser, $settings));
|
||||
})->add(new CspHeadersMiddleware($settings, $cspFromPlugins, $cspFromTheme))->add(new WebRedirectIfAuthenticated($routeParser, $settings));
|
||||
|
||||
# AUTHOR AREA (requires authentication)
|
||||
$app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl) {
|
||||
@@ -51,7 +52,7 @@ $app->group('/tm', function (RouteCollectorProxy $group) use ($routeParser,$acl)
|
||||
$group->get('/content/visual[/{route:.*}]', ControllerWebAuthor::class . ':showBlox')->setName('content.visual')->add(new WebAuthorization($routeParser, $acl, 'mycontent', 'view'));
|
||||
$group->get('/content/raw[/{route:.*}]', ControllerWebAuthor::class . ':showRaw')->setName('content.raw')->add(new WebAuthorization($routeParser, $acl, 'mycontent', 'view'));
|
||||
|
||||
})->add(new WebRedirectIfUnauthenticated($routeParser));
|
||||
})->add(new CspHeadersMiddleware($settings, $cspFromPlugins, $cspFromTheme))->add(new WebRedirectIfUnauthenticated($routeParser));
|
||||
|
||||
$app->redirect('/tm', $routeParser->urlFor('auth.show'), 302);
|
||||
$app->redirect('/tm/', $routeParser->urlFor('auth.show'), 302);
|
||||
@@ -74,11 +75,11 @@ if(isset($routes['web']) && !empty($routes['web']))
|
||||
|
||||
if($resources && $privilege)
|
||||
{
|
||||
$app->{$method}($route, $class)->setName($name)->add(new WebAuthorization($routeParser, $acl, $resource, $privilege))->add(new WebRedirectIfUnauthenticated($routeParser));
|
||||
$app->{$method}($route, $class)->setName($name)->add(new CspHeadersMiddleware($settings, $cspFromPlugins, $cspFromTheme))->add(new WebAuthorization($routeParser, $acl, $resource, $privilege))->add(new WebRedirectIfUnauthenticated($routeParser));
|
||||
}
|
||||
else
|
||||
{
|
||||
$app->{$method}($route, $class)->setName($name);
|
||||
$app->{$method}($route, $class)->setName($name)->add(new CspHeadersMiddleware($settings, $cspFromPlugins, $cspFromTheme));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,10 +87,10 @@ if(isset($routes['web']) && !empty($routes['web']))
|
||||
if(isset($settings['access']) && $settings['access'] != '')
|
||||
{
|
||||
# if access for website is restricted
|
||||
$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home')->add(new WebAuthorization($routeParser, $acl, 'account', 'view'));
|
||||
$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home')->add(new CspHeadersMiddleware($settings, $cspFromPlugins, $cspFromTheme))->add(new WebAuthorization($routeParser, $acl, 'account', 'view'));
|
||||
}
|
||||
else
|
||||
{
|
||||
# if access is not restricted
|
||||
$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home');
|
||||
$app->get('/[{route:.*}]', ControllerWebFrontend::class . ':index')->setName('home')->add(new CspHeadersMiddleware($settings, $cspFromPlugins, $cspFromTheme));
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ use Typemill\Events\OnPluginsLoaded;
|
||||
use Typemill\Events\OnSessionSegmentsLoaded;
|
||||
use Typemill\Events\OnRolesPermissionsLoaded;
|
||||
use Typemill\Events\OnResourcesLoaded;
|
||||
use Typemill\Events\OnCspLoaded;
|
||||
use Typemill\Middleware\RemoveCredentialsMiddleware;
|
||||
use Typemill\Middleware\SessionMiddleware;
|
||||
use Typemill\Middleware\OldInputMiddleware;
|
||||
@@ -378,9 +379,25 @@ if(isset($settings['proxy']) && $settings['proxy'])
|
||||
$timer['middleware'] = microtime(true);
|
||||
|
||||
|
||||
/******************************
|
||||
* GET CSP FROM PLUGINS/THEMES *
|
||||
******************************/
|
||||
|
||||
# get additional csp headers from plugins
|
||||
$cspFromPlugins = $dispatcher->dispatch(new OnCspLoaded([]), 'onCspLoaded')->getData();
|
||||
|
||||
# get additional csp headers from theme
|
||||
$cspFromTheme = [];
|
||||
$themeSettings = $settingsModel->getObjectSettings('themes', $settings['theme']);
|
||||
if(isset($themeSettings['csp']) && is_array($themeSettings['csp']) && !empty($themeSettings['csp']) )
|
||||
{
|
||||
$cspFromTheme = $themeSettings['csp'];
|
||||
}
|
||||
|
||||
/************************
|
||||
* ADD ROUTES *
|
||||
************************/
|
||||
|
||||
if(isset($settings['setup']) && $settings['setup'] == true)
|
||||
{
|
||||
require __DIR__ . '/routes/setup.php';
|
||||
@@ -393,6 +410,7 @@ else
|
||||
|
||||
$timer['routes'] = microtime(true);
|
||||
|
||||
|
||||
/************************
|
||||
* RUN APP *
|
||||
************************/
|
||||
|
Reference in New Issue
Block a user