Skip to content

Commit

Permalink
Merge pull request #5949 from WoltLab/require-https
Browse files Browse the repository at this point in the history
Make HTTPS a hard requirement
  • Loading branch information
dtdesign committed Jun 24, 2024
2 parents 24e5aa5 + 9e196bc commit ea13abe
Show file tree
Hide file tree
Showing 16 changed files with 305 additions and 93 deletions.
27 changes: 27 additions & 0 deletions wcfsetup/install/files/acp/templates/systemCheck.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
<section class="section">
<h2 class="sectionTitle">{lang}wcf.acp.systemCheck.result{/lang}</h2>

<dl{if !$results[status][web]} class="formError"{/if}>
<dt>{lang}wcf.acp.systemCheck.web{/lang}</dt>
<dd>
{if $results[status][web]}
{@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
{else}
{@$statusInsufficient} {lang}wcf.acp.systemCheck.insufficient{/lang}
{/if}
</dd>
</dl>

<dl{if !$results[status][php]} class="formError"{/if}>
<dt>{lang}wcf.acp.systemCheck.php{/lang}</dt>
<dd>
Expand Down Expand Up @@ -51,6 +62,22 @@
</dl>
</section>

<section class="section">
<h2 class="sectionTitle">{lang}wcf.acp.systemCheck.web{/lang}</h2>

<dl{if !$results[web][https]} class="formError"{/if}>
<dt>{lang}wcf.acp.systemCheck.web.https{/lang}</dt>
<dd>
{if $results[web][https]}
{@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
{else}
{@$statusInsufficient} {lang}wcf.acp.systemCheck.notSupported{/lang}
{/if}
<small>{lang}wcf.acp.systemCheck.web.https.description{/lang}</small>
</dd>
</dl>
</section>

<section class="section">
<h2 class="sectionTitle">{lang}wcf.acp.systemCheck.php{/lang}</h2>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/**
* Checks the system requirements for the upgrade from WoltLab Suite 6.0.
*
* @author Alexander Ebert
* @copyright 2001-2024 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.1
*/

use wcf\system\request\RouteHandler;
use wcf\system\WCF;

$checkForTls = function () {
if (RouteHandler::secureConnection()) {
return true;
}

// @see RouteHandler::secureContext()
$host = $_SERVER['HTTP_HOST'];
if ($host === '127.0.0.1' || $host === 'localhost' || \str_ends_with($host, '.localhost')) {
return true;
}

return false;
};

if (!$checkForTls()) {
if (WCF::getLanguage()->getFixedLanguageCode() === 'de') {
$message = "Die Seite wird nicht über HTTPS aufgerufen. Wichtige Funktionen stehen dadurch nicht zur Verfügung, die für die korrekte Funktionsweise der Software erforderlich sind.";
} else {
$message = "The page is not accessed via HTTPS. Important features that are required for the proper operation of the software are therefore not available.";
}

throw new \RuntimeException($message);
}
16 changes: 16 additions & 0 deletions wcfsetup/install/files/lib/acp/page/SystemCheckPage.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use wcf\system\Environment;
use wcf\system\exception\SystemException;
use wcf\system\registry\RegistryHandler;
use wcf\system\request\RouteHandler;
use wcf\system\WCF;
use wcf\util\FileUtil;

Expand Down Expand Up @@ -150,10 +151,14 @@ class SystemCheckPage extends AbstractPage
],
'x64' => false,
],
'web' => [
'https' => false,
],
'status' => [
'directories' => false,
'mysql' => false,
'php' => false,
'web' => false,
],
];

Expand Down Expand Up @@ -184,6 +189,7 @@ public function readData()
$this->validatePhpVersion();
$this->validatePhpGdSupport();
$this->validateWritableDirectories();
$this->validateWebHttps();

if (
$this->results['status']['mysql']
Expand Down Expand Up @@ -449,4 +455,14 @@ protected function makeDirectoryWritable($path)

return true;
}

/**
* @since 6.1
*/
protected function validateWebHttps(): void
{
$this->results['web']['https'] = RouteHandler::secureContext();

$this->results['status']['web'] = $this->results['web']['https'];
}
}
6 changes: 3 additions & 3 deletions wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ static function (\wcf\event\acp\dashboard\box\BoxCollecting $event) {
\wcf\event\endpoint\ControllerCollecting::class,
static function (\wcf\event\endpoint\ControllerCollecting $event) {
$event->register(new \wcf\system\endpoint\controller\core\files\DeleteFile);
$event->register(new \wcf\system\endpoint\controller\core\files\PostGenerateThumbnails);
$event->register(new \wcf\system\endpoint\controller\core\files\PostUpload);
$event->register(new \wcf\system\endpoint\controller\core\files\upload\PostChunk);
$event->register(new \wcf\system\endpoint\controller\core\files\GenerateThumbnails);
$event->register(new \wcf\system\endpoint\controller\core\files\PrepareUpload);
$event->register(new \wcf\system\endpoint\controller\core\files\upload\SaveChunk);
$event->register(new \wcf\system\endpoint\controller\core\comments\CreateComment);
$event->register(new \wcf\system\endpoint\controller\core\comments\DeleteComment);
$event->register(new \wcf\system\endpoint\controller\core\comments\EditComment);
Expand Down
46 changes: 46 additions & 0 deletions wcfsetup/install/files/lib/http/middleware/CheckForTls.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace wcf\http\middleware;

use Laminas\Diactoros\Response\RedirectResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use wcf\system\request\RequestHandler;
use wcf\system\request\RouteHandler;
use wcf\util\HeaderUtil;

/**
* Checks if the request is for the frontend and originates from an insecure context.
*
* @author Alexander Ebert
* @copyright 2001-2024 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.1
*/
final class CheckForTls implements MiddlewareInterface
{
#[\Override]
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (RequestHandler::getInstance()->isACPRequest()) {
return $handler->handle($request);
}

if (RouteHandler::secureContext()) {
return $handler->handle($request);
}

return $this->redirectToHttps($request);
}

private function redirectToHttps(ServerRequestInterface $request): ResponseInterface
{
$uri = $request->getUri()->withScheme('https');

return HeaderUtil::withNoCacheHeaders(
new RedirectResponse($uri)
);
}
}
2 changes: 1 addition & 1 deletion wcfsetup/install/files/lib/system/WCFSetup.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ protected function showSystemRequirements(): ResponseInterface

$system['cookie']['result'] = !empty($_COOKIE['wcfsetup_cookietest']) && $_COOKIE['wcfsetup_cookietest'] == TMP_FILE_PREFIX;

$system['tls']['result'] = RouteHandler::secureConnection() || $system['hostname']['value'] == 'localhost';
$system['tls']['result'] = RouteHandler::secureContext();

foreach ($system as $result) {
if (!$result['result']) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use wcf\system\Environment;
use wcf\system\event\EventHandler;
use wcf\system\registry\RegistryHandler;
use wcf\system\request\RouteHandler;
use wcf\system\WCF;

/**
Expand Down Expand Up @@ -147,6 +148,13 @@ private function getBasicMessages(): array
);
}

if (!RouteHandler::secureContext()) {
$messages[] = new StatusMessage(
StatusMessageType::Error,
WCF::getLanguage()->getDynamicVariable('wcf.acp.index.insecureContext')
);
}

return $messages;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
use wcf\http\Helper;
use wcf\system\endpoint\IController;
use wcf\system\endpoint\PostRequest;
use wcf\system\exception\UserInputException;
use wcf\system\file\processor\FileProcessor;

#[PostRequest('/core/files/{id:\d+}/generatethumbnails')]
final class PostGenerateThumbnails implements IController
final class GenerateThumbnails implements IController
{
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use wcf\util\JSON;

#[PostRequest('/core/files/upload')]
final class PostUpload implements IController
final class PrepareUpload implements IController
{
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use wcf\system\io\File;

#[PostRequest('/core/files/upload/{identifier}/chunk/{sequenceNo:\d+}')]
final class PostChunk implements IController
final class SaveChunk implements IController
{
/**
* Read data in chunks to avoid hitting the memory limit.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use wcf\http\middleware\CheckForForceLogin;
use wcf\http\middleware\CheckForMultifactorRequirement;
use wcf\http\middleware\CheckForOfflineMode;
use wcf\http\middleware\CheckForTls;
use wcf\http\middleware\CheckHttpMethod;
use wcf\http\middleware\CheckSystemEnvironment;
use wcf\http\middleware\CheckUserBan;
Expand Down Expand Up @@ -144,6 +145,7 @@ public function handle(string $application = 'wcf', bool $isACPRequest = false):
new EnforceAcpAuthentication(),
new CheckForEnterpriseNonOwnerAccess(),
new CheckForExpiredAppEvaluation(),
new CheckForTls(),
new CheckForOfflineMode(),
new CheckForForceLogin(),
new CheckForMultifactorRequirement(),
Expand Down
28 changes: 28 additions & 0 deletions wcfsetup/install/files/lib/system/request/RouteHandler.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,34 @@ public static function secureConnection(): bool
return self::$secure;
}

/**
* Returns true if the current environment is treated as a secure context by
* browsers.
*
* @see https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure
* @since 6.1
*/
public static function secureContext(): bool
{
static $secureContext = null;
if ($secureContext === null) {
$secureContext = self::secureConnection();

// The connection is considered as secure if it is encrypted with
// TLS, or if the target host is a local address.
if (!$secureContext) {
$host = $_SERVER['HTTP_HOST'];

// @see https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-let-localhost-be-localhost-02
if ($host === '127.0.0.1' || $host === 'localhost' || \str_ends_with($host, '.localhost')) {
$secureContext = true;
}
}
}

return $secureContext;
}

/**
* Returns HTTP protocol, either 'http://' or 'https://'.
*/
Expand Down
4 changes: 4 additions & 0 deletions wcfsetup/install/lang/de.xml
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,7 @@ Sie erreichen das Fehlerprotokoll unter: {link controller='ExceptionLogView' isE
<item name="wcf.acp.index.missingLanguageItems"><![CDATA[Es wurden fehlende Sprachvariablen protokolliert (zuletzt: {@$missingLanguageItemsMTime|time}). {if LANGUAGE_USE_INFORMAL_VARIANT}Überprüfe{else}Überprüfen Sie{/if} die <a href="{link controller='DevtoolsMissingLanguageItemList'}{/link}">Liste der fehlenden Texte</a> für weitere Informationen.]]></item>
<item name="wcf.acp.index.systemIdMismatch"><![CDATA[Die Systemumgebung hat sich verändert. Es wird empfohlen, eine <a href="{link controller='SystemCheck'}{/link}">Systemüberprüfung</a> durchzuführen.]]></item>
<item name="wcf.acp.index.cacheSanityCheckFailed"><![CDATA[Die eingestellte Cache-Methode („{CACHE_SOURCE_TYPE}“) funktioniert nicht richtig.]]></item>
<item name="wcf.acp.index.insecureContext"><![CDATA[Die Seite wird nicht über HTTPS aufgerufen. Wichtige Funktionen stehen dadurch nicht zur Verfügung, die für die korrekte Funktionsweise der Software erforderlich sind.]]></item>
</category>
<category name="wcf.acp.label">
<item name="wcf.acp.label.add"><![CDATA[Label hinzufügen]]></item>
Expand Down Expand Up @@ -2815,6 +2816,9 @@ Abschnitte dürfen nicht leer sein und nur folgende Zeichen enthalten: <kbd>[a-z
<item name="wcf.acp.systemCheck.php.opcache"><![CDATA[OPcache]]></item>
<item name="wcf.acp.systemCheck.php.opcache.description"><![CDATA[PHPs OPcache beschleunigt die Bearbeitung von Anfragen, indem der vorverarbeitete Programmcode gecached wird. Bei aktiviertem OPcache müssen die Verwaltungsfunktionen <kbd>opcache_reset()</kbd> und <kbd>opcache_invalidate()</kbd> zur Verfügung stehen, damit der Cache nach einer Aktualisierung des Programmcodes zuverlässig neu aufgebaut werden kann.]]></item>
<item name="wcf.acp.systemCheck.php.opcache.broken"><![CDATA[Fehlerhaft – die Verwaltungsfunktionen sind nicht verfügbar]]></item>
<item name="wcf.acp.systemCheck.web"><![CDATA[Webserver]]></item>
<item name="wcf.acp.systemCheck.web.https"><![CDATA[Aufruf per HTTPS/TLS]]></item>
<item name="wcf.acp.systemCheck.web.https.description"><![CDATA[Neue Funktionen in Webbrowsern werden nur beim Aufruf über HTTPS/TLS unterstützt, bei unverschlüsselten Verbindungen fehlen wichtige Funktionen.]]></item>
</category>
<category name="wcf.acp.updateServer">
<item name="wcf.acp.updateServer.add"><![CDATA[Server hinzufügen]]></item>
Expand Down
4 changes: 4 additions & 0 deletions wcfsetup/install/lang/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ You can access the error log at: {link controller='ExceptionLogView' isEmail=tru
<item name="wcf.acp.index.missingLanguageItems"><![CDATA[Missing language items have been detected (last time: {@$missingLanguageItemsMTime|time}). Check the <a href="{link controller='DevtoolsMissingLanguageItemList'}{/link}">list of missing phrases</a> for more information.]]></item>
<item name="wcf.acp.index.systemIdMismatch"><![CDATA[The system environment has changed. It is recommended to perform a <a href="{link controller='SystemCheck'}{/link}">System Check</a>.]]></item>
<item name="wcf.acp.index.cacheSanityCheckFailed"><![CDATA[The configured caching method (“{CACHE_SOURCE_TYPE}”) is not working correctly.]]></item>
<item name="wcf.acp.index.insecureContext"><![CDATA[The page is not accessed via HTTPS. Important features that are required for the proper operation of the software are therefore not available.]]></item>
</category>
<category name="wcf.acp.label">
<item name="wcf.acp.label.add"><![CDATA[Add Label]]></item>
Expand Down Expand Up @@ -2744,6 +2745,9 @@ If you have <strong>already bought the licenses for the listed apps</strong>, th
<item name="wcf.acp.systemCheck.php.opcache"><![CDATA[OPcache]]></item>
<item name="wcf.acp.systemCheck.php.opcache.description"><![CDATA[PHP’s OPcache improves request processing performance by caching the preprocessed application code. If OPcache is enabled the management functions <kbd>opcache_reset()</kbd> and <kbd>opcache_invalidate()</kbd> need to be available to be able to reliably reset the cache when the application code is updated.]]></item>
<item name="wcf.acp.systemCheck.php.opcache.broken"><![CDATA[Broken – the management functions are unavailable]]></item>
<item name="wcf.acp.systemCheck.web"><![CDATA[Web Server]]></item>
<item name="wcf.acp.systemCheck.web.https"><![CDATA[Access via HTTPS/TLS]]></item>
<item name="wcf.acp.systemCheck.web.https.description"><![CDATA[New functions in web browsers are only supported when accessed via HTTPS/TLS; important features are missing for unencrypted connections.]]></item>
</category>
<category name="wcf.acp.updateServer">
<item name="wcf.acp.updateServer.add"><![CDATA[Add Server]]></item>
Expand Down
14 changes: 7 additions & 7 deletions wcfsetup/setup/template/stepShowSystemRequirements.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,13 @@
</dl>
</div>
</section>
</section>

<section class="section">
<h2 class="sectionTitle">{lang}wcf.global.systemRequirements.recommended{/lang}</h2>


<section class="section">
<h2 class="sectionTitle">{lang}wcf.global.systemRequirements.tls{/lang}</h2>

<div class="row rowColGap formGrid">
<dl class="col-xs-12 col-md-6">
<dt>{lang}wcf.global.systemRequirements.element.recommended{/lang}</dt>
<dt>{lang}wcf.global.systemRequirements.element.required{/lang}</dt>
<dd>{lang}wcf.global.systemRequirements.active{/lang}</dd>
</dl>

Expand All @@ -197,6 +193,10 @@
</dl>
</div>
</section>
</section>

<section class="section">
<h2 class="sectionTitle">{lang}wcf.global.systemRequirements.recommended{/lang}</h2>

<section class="section">
<h2 class="sectionTitle">{lang}wcf.global.systemRequirements.uploadMaxFilesize{/lang}</h2>
Expand Down Expand Up @@ -263,7 +263,7 @@
</section>

<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.next{/lang}"{if !$system.phpVersion.result || !$system.x64.result || !$system.sql.result || !$system.memoryLimit.result || !$system.graphicsLibrary.result || !$system.hostname.result || !$system.cookie.result} disabled{/if} accesskey="s">
<input type="submit" value="{lang}wcf.global.button.next{/lang}"{if !$system.phpVersion.result || !$system.x64.result || !$system.sql.result || !$system.memoryLimit.result || !$system.graphicsLibrary.result || !$system.hostname.result || !$system.cookie.result || !$system.tls.result} disabled{/if} accesskey="s">
<input type="hidden" name="step" value="{$nextStep}">
<input type="hidden" name="tmpFilePrefix" value="{$tmpFilePrefix}">
<input type="hidden" name="languageCode" value="{$languageCode}">
Expand Down
Loading

0 comments on commit ea13abe

Please sign in to comment.