diff --git a/.htaccess b/.htaccess index 4b254b0..7d28183 100644 --- a/.htaccess +++ b/.htaccess @@ -36,6 +36,5 @@ FileETag None Header set X-Content-Type-Options nosniff Header set X-XSS-Protection "1; mode=block" Header set Referrer-Policy no-referrer - Header set Content-Security-Policy "default-src 'self'; object-src 'none'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src http:" Header add Link "; rel=preload, ; rel=preload" "expr=%{CONTENT_TYPE} =~ m#text/html#" diff --git a/classes/Controller/FrontController.php b/classes/Controller/FrontController.php index 5590860..f46e326 100644 --- a/classes/Controller/FrontController.php +++ b/classes/Controller/FrontController.php @@ -6,6 +6,7 @@ namespace Alltube\Controller; +use Alltube\CspMiddleware; use Alltube\Library\Exception\PasswordException; use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Library\Exception\WrongPasswordException; @@ -295,6 +296,12 @@ class FrontController extends BaseController { $this->logger->error($error); + // We apply the CSP manually because middlewares are not called on error pages. + $cspMiddleware = new CspMiddleware($this->container); + + /** @var Response $response */ + $response = $cspMiddleware->applyHeader($response); + if ($this->config->debug) { $renderer = new HtmlErrorRenderer(true); $exception = $renderer->render($error); diff --git a/classes/CspMiddleware.php b/classes/CspMiddleware.php new file mode 100644 index 0000000..e80103a --- /dev/null +++ b/classes/CspMiddleware.php @@ -0,0 +1,65 @@ +config = $container->get('config'); + } + + /** + * @param Response $response + * @return MessageInterface + */ + public function applyHeader(Response $response) + { + $csp = new CSPBuilder(); + $csp->addDirective('default-src', []) + ->addDirective('font-src', ['self' => true]) + ->addDirective('style-src', ['self' => true]) + ->addSource('img-src', '*'); + + if ($this->config->debug) { + // So symfony/debug and symfony/error-handler can work. + $csp->setDirective('script-src', ['unsafe-inline' => true]) + ->setDirective('style-src', ['self' => true, 'unsafe-inline' => true]); + } + + return $csp->injectCSPHeader($response); + } + + /** + * @param Request $request + * @param Response $response + * @param callable $next + * @return mixed + */ + public function __invoke(Request $request, Response $response, callable $next) + { + $response = $this->applyHeader($response); + + return $next($request, $response); + } +} diff --git a/composer.json b/composer.json index ec460d2..1d9cc5e 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "jawira/case-converter": "^3.4", "jean85/pretty-package-versions": "^1.3", "mathmarques/smarty-view": "^1.1", + "paragonie/csp-builder": "^2.5", "rinvex/countries": "^6.1", "rudloff/alltube-library": "dev-develop", "symfony/finder": "^5.0", diff --git a/composer.lock b/composer.lock index 769bab3..301cfb4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7e756e8b0f372bd5a914c54310933ecb", + "content-hash": "c0d33bde893f1ac7cbfcd26d26a92c9f", "packages": [ { "name": "aura/session", @@ -618,6 +618,124 @@ ], "time": "2018-02-13T20:26:39+00:00" }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2", + "reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7", + "vimeo/psalm": "^1|^2|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "time": "2019-11-06T19:20:29+00:00" + }, + { + "name": "paragonie/csp-builder", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/csp-builder.git", + "reference": "73ebd90199eb6f3be6549d5390a7698c6deffa30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/csp-builder/zipball/73ebd90199eb6f3be6549d5390a7698c6deffa30", + "reference": "73ebd90199eb6f3be6549d5390a7698c6deffa30", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^2", + "php": "^7.1|^8" + }, + "require-dev": { + "phpunit/phpunit": "^7|^8|^9", + "psr/http-message": "^1", + "squizlabs/php_codesniffer": "^3", + "vimeo/psalm": "^3" + }, + "suggest": { + "psr/http-message": "For CSPBuilder::injectCSPHeader()" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\CSPBuilder\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Owner" + } + ], + "description": "Easily add and update Content-Security-Policy headers for your project", + "keywords": [ + "content-security-policy", + "csp", + "headers", + "http", + "security", + "xss" + ], + "time": "2020-09-02T14:53:15+00:00" + }, { "name": "pimple/pimple", "version": "v3.3.0", diff --git a/index.php b/index.php index 02c8eb8..f40d7fd 100644 --- a/index.php +++ b/index.php @@ -6,6 +6,7 @@ use Alltube\ConfigFactory; use Alltube\Controller\DownloadController; use Alltube\Controller\FrontController; use Alltube\Controller\JsonController; +use Alltube\CspMiddleware; use Alltube\ErrorHandler; use Alltube\LocaleManagerFactory; use Alltube\LocaleMiddleware; @@ -42,6 +43,7 @@ try { // Middlewares. $app->add(new LocaleMiddleware($container)); $app->add(new RouterPathMiddleware($container)); + $app->add(new CspMiddleware($container)); // Controllers. $frontController = new FrontController($container);