Skip to content

Commit

Permalink
[FEATURE] Enable secure cookies by default
Browse files Browse the repository at this point in the history
The option $TYPO3_CONF_VARS[SYS][cookieSecure] is
removed in favor of always setting a secure cookie
on HTTPS requests.

This leads to errors when a page would be available
in HTTP and HTTPS which is normally not the case
when using a full site base in Site Handling anymore,
and making TYPO3 more secure out-of-the-box.

Resolves: #87301
Releases: master
Change-Id: Iba90c19456af6a82feb9c53fea52228fbff516be
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/65695
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Frank Nägler <frank.naegler@typo3.org>
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Frank Nägler <frank.naegler@typo3.org>
Reviewed-by: Jörg Bösche <typo3@joergboesche.de>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Benni Mack <benni@typo3.org>
  • Loading branch information
bmack committed Sep 11, 2020
1 parent 27593da commit b180b54
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 37 deletions.
4 changes: 1 addition & 3 deletions typo3/sysext/backend/Classes/Controller/LoginController.php
Expand Up @@ -713,15 +713,13 @@ protected function detectLoginProvider(ServerRequestInterface $request): string
// Use the secure option when the current request is served by a secure connection
/** @var NormalizedParams $normalizedParams */
$normalizedParams = $request->getAttribute('normalizedParams');
$isHttps = $normalizedParams->isHttps();
$cookieSecure = (bool)$GLOBALS['TYPO3_CONF_VARS']['SYS']['cookieSecure'] && $isHttps;
$cookie = new Cookie(
'be_lastLoginProvider',
$loginProvider,
$GLOBALS['EXEC_TIME'] + 7776000, // 90 days
$normalizedParams->getSitePath() . TYPO3_mainDir,
'',
$cookieSecure,
$normalizedParams->isHttps(),
true,
false,
Cookie::SAMESITE_STRICT
Expand Down
Expand Up @@ -453,34 +453,26 @@ protected function setSessionCookie()
$cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
// If the cookie lifetime is set, use it:
$cookieExpire = $isRefreshTimeBasedCookie ? $GLOBALS['EXEC_TIME'] + $this->lifetime : 0;
// Use the secure option when the current request is served by a secure connection:
$cookieSecure = (bool)$settings['cookieSecure'] && GeneralUtility::getIndpEnv('TYPO3_SSL');
// Valid options are "strict", "lax" or "none", whereas "none" only works in HTTPS requests (default & fallback is "strict")
$cookieSameSite = $this->sanitizeSameSiteCookieValue(
strtolower($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieSameSite'] ?? Cookie::SAMESITE_STRICT)
);
// Use the secure option when the current request is served by a secure connection:
// SameSite "none" needs the secure option (only allowed on HTTPS)
if ($cookieSameSite === Cookie::SAMESITE_NONE) {
$cookieSecure = true;
}
// Do not set cookie if cookieSecure is set to "1" (force HTTPS) and no secure channel is used:
if ((int)$settings['cookieSecure'] !== 1 || GeneralUtility::getIndpEnv('TYPO3_SSL')) {
$cookie = new Cookie(
$this->name,
$this->id,
$cookieExpire,
$cookiePath,
$cookieDomain,
$cookieSecure,
true,
false,
$cookieSameSite
);
header('Set-Cookie: ' . $cookie->__toString(), false);
$this->cookieWasSetOnCurrentRequest = true;
} else {
throw new Exception('Cookie was not set since HTTPS was forced in $TYPO3_CONF_VARS[SYS][cookieSecure].', 1254325546);
}
$isSecure = $cookieSameSite === Cookie::SAMESITE_NONE || GeneralUtility::getIndpEnv('TYPO3_SSL');
$cookie = new Cookie(
$this->name,
$this->id,
$cookieExpire,
$cookiePath,
$cookieDomain,
$isSecure,
true,
false,
$cookieSameSite
);
header('Set-Cookie: ' . $cookie->__toString(), false);
$this->cookieWasSetOnCurrentRequest = true;
$this->logger->debug(
($isRefreshTimeBasedCookie ? 'Updated Cookie: ' : 'Set Cookie: ')
. $this->id . ($cookieDomain ? ', ' . $cookieDomain : '')
Expand Down
1 change: 0 additions & 1 deletion typo3/sysext/core/Configuration/DefaultConfiguration.php
Expand Up @@ -83,7 +83,6 @@
'sitename' => 'TYPO3',
'encryptionKey' => '',
'cookieDomain' => '',
'cookieSecure' => 0,
'trustedHostsPattern' => 'SERVER_NAME',
'devIPmask' => '127.0.0.1,::1',
'ddmmyy' => 'd-m-y',
Expand Down
Expand Up @@ -84,13 +84,6 @@ SYS:
cookieDomain:
type: text
description: 'Restricts the domain name for FE and BE session cookies. When setting the value to ".domain.com" (replace domain.com with your domain!), login sessions will be shared across subdomains. Alternatively, if you have more than one domain with sub-domains, you can set the value to a regular expression to match against the domain of the HTTP request. The result of the match is used as the domain for the cookie. eg. <code>/\.(example1|example2)\.com$/</code> or <code>/\.(example1\.com)|(example2\.net)$/</code>. Separate domains for FE and BE can be set using <a href="#FE-cookieDomain">$TYPO3_CONF_VARS[''FE''][''cookieDomain'']</a> and <a href="#BE-cookieDomain">$TYPO3_CONF_VARS[''BE''][''cookieDomain'']</a> respectively.'
cookieSecure:
type: int
allowedValues:
'0': 'Always send the cookie, regardless if the connection is secure'
'1': 'Force HTTPS: the cookie will only be set if a secure (HTTPS) connection exists - use this in combination with lockSSL since otherwise the application will fail and throw an exception'
'2': 'The cookie will be set in each case, but uses the secure flag if a secure (HTTPS) connection exists'
description: 'Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client.'
trustedHostsPattern:
type: text
description: 'Regular expression pattern that matches all allowed hostnames (including their ports) of this TYPO3 installation, or the string "SERVER_NAME" (default). The default value <code>SERVER_NAME</code> checks if the HTTP Host header equals the SERVER_NAME and SERVER_PORT. This is secure in correctly configured hosting environments and does not need further configuration. If you cannot change your hosting environment, you can enter a regular expression here. Examples: <code>.*\.domain\.com</code> matches all hosts that end with <code>.domain.com</code> with all corresponding subdomains. <code>(.*\.domain|.*\.otherdomain)\.com</code> matches all hostnames with subdomains from <code>.domain.com</code> and <code>.otherdomain.com</code>. Be aware that HTTP Host header may also contain a port. If your installation runs on a specific port, you need to explicitly allow this in your pattern, e.g. <code>www\.domain\.com:88</code> allows only <code>www.domain.com:88</code>, <strong>not</strong> <code>www.domain.com</code>. To disable this check completely (not recommended because it is <strong>insecure</strong>) you can use ".*" as pattern.'
Expand Down
@@ -0,0 +1,33 @@
.. include:: ../../Includes.txt

===================================================
Feature: #87301 - Secure cookies enabled by default
===================================================

See :issue:`87301`

Description
===========

In previous TYPO3 installations there was a option to define
whether a cookie was shared between HTTP and HTTPS requests.

This allowed to have the same cookie available for HTTPS and non-HTTPS, when a site was available on both ports / protocols.

In order to enhance security, the option is removed and the feature
provides sensible defaults in the current state of the web, where
it is recommended to run sites with HTTPS, or if this is not possible
to use HTTP, but not using a mixed mode, which also has SEO downsides.


Impact
======

The new defaults are:

* If a website is running on HTTPS, the cookie is only exposed via HTTPS.
* If a website is running on HTTP, the cookie is available for HTTPS as well, but not vice-versa.

The TYPO3 Configuration option `$TYPO3_CONF_VARS[SYS][cookieSecure]` is removed when upgrading TYPO3 installations.

.. index:: LocalConfiguration, ext:core
Expand Up @@ -157,6 +157,8 @@ class SilentConfigurationUpgradeService
'SYS/systemLogLevel',
// #91974
'FE/IPmaskMountGroups',
// #87301
'SYS/cookieSecure',
];

public function __construct(ConfigurationManager $configurationManager)
Expand Down
Expand Up @@ -238,16 +238,16 @@ protected function addCookie(string $keyword, NormalizedParams $normalizedParams
$cookieSameSite = $this->sanitizeSameSiteCookieValue(
strtolower($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieSameSite'] ?? Cookie::SAMESITE_STRICT)
);
// None needs the secure option (only allowed on HTTPS)
$cookieSecure = $cookieSameSite === Cookie::SAMESITE_NONE || $normalizedParams->isHttps();
// SameSite Cookie = None needs the secure option (only allowed on HTTPS)
$isSecure = $cookieSameSite === Cookie::SAMESITE_NONE || $normalizedParams->isHttps();

$cookie = new Cookie(
$this->previewKey,
$keyword,
0,
$normalizedParams->getSitePath(),
null,
$cookieSecure,
$isSecure,
true,
false,
$cookieSameSite
Expand Down

0 comments on commit b180b54

Please sign in to comment.