aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorCôme Chilliet <come.chilliet@nextcloud.com>2024-03-07 17:38:24 +0100
committerFerdinand Thiessen <opensource@fthiessen.de>2024-03-13 12:49:52 +0100
commita47a1e6249f97765d7f899c8dfb9b44c76b0d542 (patch)
tree4d438904f4c26c782c2643783b9167805a31f4f0 /apps
parent0ce35c707f81c56678bb8533bfb9b64af8cc84bf (diff)
downloadnextcloud-server-a47a1e6249f97765d7f899c8dfb9b44c76b0d542.tar.gz
nextcloud-server-a47a1e6249f97765d7f899c8dfb9b44c76b0d542.zip
feat: Migrate header check to SetupCheck API
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Diffstat (limited to 'apps')
-rw-r--r--apps/settings/composer/composer/autoload_classmap.php1
-rw-r--r--apps/settings/composer/composer/autoload_static.php1
-rw-r--r--apps/settings/lib/AppInfo/Application.php2
-rw-r--r--apps/settings/lib/SetupChecks/SecurityHeaders.php134
4 files changed, 138 insertions, 0 deletions
diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php
index b9709c8ad28..17e47f62a7d 100644
--- a/apps/settings/composer/composer/autoload_classmap.php
+++ b/apps/settings/composer/composer/autoload_classmap.php
@@ -117,6 +117,7 @@ return array(
'OCA\\Settings\\SetupChecks\\PushService' => $baseDir . '/../lib/SetupChecks/PushService.php',
'OCA\\Settings\\SetupChecks\\RandomnessSecure' => $baseDir . '/../lib/SetupChecks/RandomnessSecure.php',
'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => $baseDir . '/../lib/SetupChecks/ReadOnlyConfig.php',
+ 'OCA\\Settings\\SetupChecks\\SecurityHeaders' => $baseDir . '/../lib/SetupChecks/SecurityHeaders.php',
'OCA\\Settings\\SetupChecks\\SupportedDatabase' => $baseDir . '/../lib/SetupChecks/SupportedDatabase.php',
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => $baseDir . '/../lib/SetupChecks/SystemIs64bit.php',
'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => $baseDir . '/../lib/SetupChecks/TempSpaceAvailable.php',
diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php
index 67808ad23f2..1dccc69b923 100644
--- a/apps/settings/composer/composer/autoload_static.php
+++ b/apps/settings/composer/composer/autoload_static.php
@@ -132,6 +132,7 @@ class ComposerStaticInitSettings
'OCA\\Settings\\SetupChecks\\PushService' => __DIR__ . '/..' . '/../lib/SetupChecks/PushService.php',
'OCA\\Settings\\SetupChecks\\RandomnessSecure' => __DIR__ . '/..' . '/../lib/SetupChecks/RandomnessSecure.php',
'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => __DIR__ . '/..' . '/../lib/SetupChecks/ReadOnlyConfig.php',
+ 'OCA\\Settings\\SetupChecks\\SecurityHeaders' => __DIR__ . '/..' . '/../lib/SetupChecks/SecurityHeaders.php',
'OCA\\Settings\\SetupChecks\\SupportedDatabase' => __DIR__ . '/..' . '/../lib/SetupChecks/SupportedDatabase.php',
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => __DIR__ . '/..' . '/../lib/SetupChecks/SystemIs64bit.php',
'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => __DIR__ . '/..' . '/../lib/SetupChecks/TempSpaceAvailable.php',
diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php
index 0977da398b0..9f7ec3036f4 100644
--- a/apps/settings/lib/AppInfo/Application.php
+++ b/apps/settings/lib/AppInfo/Application.php
@@ -86,6 +86,7 @@ use OCA\Settings\SetupChecks\PhpOutputBuffering;
use OCA\Settings\SetupChecks\PushService;
use OCA\Settings\SetupChecks\RandomnessSecure;
use OCA\Settings\SetupChecks\ReadOnlyConfig;
+use OCA\Settings\SetupChecks\SecurityHeaders;
use OCA\Settings\SetupChecks\SupportedDatabase;
use OCA\Settings\SetupChecks\SystemIs64bit;
use OCA\Settings\SetupChecks\TempSpaceAvailable;
@@ -214,6 +215,7 @@ class Application extends App implements IBootstrap {
$context->registerSetupCheck(PhpOutputBuffering::class);
$context->registerSetupCheck(RandomnessSecure::class);
$context->registerSetupCheck(ReadOnlyConfig::class);
+ $context->registerSetupCheck(SecurityHeaders::class);
$context->registerSetupCheck(SupportedDatabase::class);
$context->registerSetupCheck(SystemIs64bit::class);
$context->registerSetupCheck(TempSpaceAvailable::class);
diff --git a/apps/settings/lib/SetupChecks/SecurityHeaders.php b/apps/settings/lib/SetupChecks/SecurityHeaders.php
new file mode 100644
index 00000000000..d5239d5a1b1
--- /dev/null
+++ b/apps/settings/lib/SetupChecks/SecurityHeaders.php
@@ -0,0 +1,134 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2023 Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Settings\SetupChecks;
+
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\SetupCheck\ISetupCheck;
+use OCP\SetupCheck\SetupResult;
+use Psr\Log\LoggerInterface;
+
+class SecurityHeaders implements ISetupCheck {
+
+ use CheckServerResponseTrait;
+
+ public function __construct(
+ protected IL10N $l10n,
+ protected IConfig $config,
+ protected IURLGenerator $urlGenerator,
+ protected IRequest $request,
+ protected IClientService $clientService,
+ protected LoggerInterface $logger,
+ ) {
+ }
+
+ public function getCategory(): string {
+ return 'security';
+ }
+
+ public function getName(): string {
+ return $this->l10n->t('HTTP headers');
+ }
+
+ public function run(): SetupResult {
+ $urls = [
+ ['get', $this->urlGenerator->linkToRoute('heartbeat'), [200]],
+ ];
+ $securityHeaders = [
+ 'X-Content-Type-Options' => ['nosniff', null],
+ 'X-Robots-Tag' => ['noindex, nofollow', null],
+ 'X-Frame-Options' => ['sameorigin', 'deny'],
+ 'X-Permitted-Cross-Domain-Policies' => ['none', null],
+ ];
+
+ foreach ($urls as [$verb,$url,$validStatuses]) {
+ $works = null;
+ foreach ($this->runRequest($url, $verb) as $response) {
+ // Check that the response status matches
+ if (!in_array($response->getStatusCode(), $validStatuses)) {
+ $works = false;
+ continue;
+ }
+ $msg = '';
+ $msgParameters = [];
+ foreach ($securityHeaders as $header => [$expected, $accepted]) {
+ $value = strtolower($response->getHeader($header));
+ if ($value !== $expected) {
+ if ($accepted !== null && $value === $accepted) {
+ $msg .= $this->l10n->t('- The `%1` HTTP header is not set to `%2`. Some features might not work correctly, as it is recommended to adjust this setting accordingly.', [$header, $expected]);
+ } else {
+ $msg .= $this->l10n->t('- The `%1` HTTP header is not set to `%2`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', [$header, $expected]);
+ }
+ }
+ }
+
+ $xssfields = array_map('trim', explode(';', $response->getHeader('X-XSS-Protection')));
+ if (!in_array('1', $xssfields) || !in_array('mode=block', $xssfields)) {
+ $msg .= $this->l10n->t('- The `%1` HTTP header does not contain `%2`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', ['X-XSS-Protection', '1; mode=block']);
+ }
+
+ $referrerPolicy = $response->getHeader('Referrer-Policy');
+ if ($referrerPolicy === null || !preg_match('/(no-referrer(-when-downgrade)?|strict-origin(-when-cross-origin)?|same-origin)(,|$)/', $referrerPolicy)) {
+ $msg .= $this->l10n->t(
+ '- The `%1` HTTP header is not set to `%2`, `%3`, `%4`, `%5` or `%6`. This can leak referer information. See the {w3c-recommendation}.',
+ [
+ 'Referrer-Policy',
+ 'no-referrer',
+ 'no-referrer-when-downgrade',
+ 'strict-origin',
+ 'strict-origin-when-cross-origin',
+ 'same-origin',
+ ]
+ );
+ $msgParameters['w3c-recommendation'] = [
+ 'type' => 'highlight',
+ 'id' => 'w3c-recommendation',
+ 'name' => 'W3C Recommendation',
+ 'link' => 'https://www.w3.org/TR/referrer-policy/',
+ ];
+ }
+ if (!empty($msg)) {
+ return SetupResult::warning($this->l10n->t('Some headers are not set correctly on your instance')."\n".$msg, descriptionParameters:$msgParameters);
+ }
+ // Skip the other requests if one works
+ break;
+ }
+ // If 'works' is null then we could not connect to the server
+ if ($works === null) {
+ return SetupResult::info(
+ $this->l10n->t('Could not check that your web server serves security headers correctly. Please check manually.'),
+ );
+ }
+ }
+ return SetupResult::success(
+ $this->l10n->t('Your server is correctly configured to send security headers.')
+ );
+ }
+}