diff --git a/classes/rex-backend-login.html b/classes/rex-backend-login.html index 5becba302..7facf34ff 100644 --- a/classes/rex-backend-login.html +++ b/classes/rex-backend-login.html @@ -1506,9 +1506,9 @@

@@ -1588,9 +1588,9 @@

Creates the user object if it does not already exist.

@@ -1628,9 +1628,9 @@

@@ -1660,9 +1660,9 @@

@@ -1692,9 +1692,9 @@

Einstellen der Cookie Paramter bevor die session gestartet wird.

@@ -1729,9 +1729,9 @@

@@ -1801,9 +1801,9 @@

@@ -1874,9 +1874,9 @@

@@ -1910,9 +1910,9 @@

Gibt den Wert einer Session-Variable zurück.

@@ -1985,9 +1985,9 @@

@@ -2021,9 +2021,9 @@

@@ -2093,9 +2093,9 @@

Gibt einen Benutzer-Spezifischen Wert zurück.

@@ -2143,9 +2143,9 @@

@@ -2179,9 +2179,9 @@

@@ -2221,9 +2221,9 @@

@@ -2290,9 +2290,9 @@

Verschlüsselt den übergebnen String.

@@ -2348,9 +2348,9 @@

@@ -2398,9 +2398,9 @@

@@ -2462,9 +2462,9 @@

refresh session on permission elevation for security reasons.

@@ -2495,9 +2495,9 @@

@@ -2937,9 +2937,9 @@

Setzte eine Session-Variable.

@@ -3162,9 +3162,9 @@

starts a http-session if not already started.

@@ -3195,9 +3195,9 @@

returns the backends session namespace.

diff --git a/classes/rex-http-exception.html b/classes/rex-http-exception.html index 55f6b6e2e..38e5a117c 100644 --- a/classes/rex-http-exception.html +++ b/classes/rex-http-exception.html @@ -204,6 +204,12 @@

 : string +
+ isClientError() + +  : bool +
+ @@ -303,6 +309,42 @@

Return values
string + +
+

+ isClientError() + + +

+ + + + + public + isClientError() : bool + +
+
+ + + + +
+
Return values
+ bool +
+
@@ -419,6 +461,7 @@
Return values
diff --git a/classes/rex-logger.html b/classes/rex-logger.html index 9c328d3f1..cd84cbc70 100644 --- a/classes/rex-logger.html +++ b/classes/rex-logger.html @@ -295,9 +295,9 @@

Closes the logfile. The logfile is not be able to log further message after beeing closed.

@@ -330,9 +330,9 @@

@@ -366,9 +366,9 @@

Map php error codes to PSR3 error levels.

@@ -452,9 +452,9 @@

Logs with an arbitrary level.

@@ -547,9 +547,9 @@

Shorthand: Logs a error message.

@@ -693,9 +693,9 @@

Prepares the logifle for later use.

diff --git a/classes/rex-login.html b/classes/rex-login.html index 05981f3a3..2d4499db2 100644 --- a/classes/rex-login.html +++ b/classes/rex-login.html @@ -1395,9 +1395,9 @@

@@ -1477,9 +1477,9 @@

@@ -1509,9 +1509,9 @@

Einstellen der Cookie Paramter bevor die session gestartet wird.

@@ -1546,9 +1546,9 @@

@@ -1619,9 +1619,9 @@

Gibt den Wert einer Session-Variable zurück.

@@ -1694,9 +1694,9 @@

@@ -1730,9 +1730,9 @@

Gibt einen Benutzer-Spezifischen Wert zurück.

@@ -1780,9 +1780,9 @@

@@ -1859,9 +1859,9 @@

Verschlüsselt den übergebnen String.

@@ -1917,9 +1917,9 @@

@@ -1967,9 +1967,9 @@

@@ -2031,9 +2031,9 @@

refresh session on permission elevation for security reasons.

@@ -2428,9 +2428,9 @@

Setzte eine Session-Variable.

@@ -2611,9 +2611,9 @@

starts a http-session if not already started.

@@ -2644,9 +2644,9 @@

returns the current session namespace.

diff --git a/classes/rex-yaml-parse-exception.html b/classes/rex-yaml-parse-exception.html index 2f37fe92a..39af42313 100644 --- a/classes/rex-yaml-parse-exception.html +++ b/classes/rex-yaml-parse-exception.html @@ -159,9 +159,9 @@

Exception class for yaml parse errors.

diff --git a/files/redaxo-main/redaxo/src/addons/metainfo/extensions/extension_cleanup.php.txt b/files/redaxo-main/redaxo/src/addons/metainfo/extensions/extension_cleanup.php.txt index 8d3280523..4b06ca427 100644 --- a/files/redaxo-main/redaxo/src/addons/metainfo/extensions/extension_cleanup.php.txt +++ b/files/redaxo-main/redaxo/src/addons/metainfo/extensions/extension_cleanup.php.txt @@ -28,9 +28,7 @@ function rex_metainfo_cleanup($epOrParams) return; } - // check wheter tables exists - $tables = rex_sql::factory()->getTables(); - if (!isset($tables[rex::getTablePrefix() . 'metainfo_field'])) { + if (!rex_sql_table::get(rex::getTable('metainfo_field'))->exists()) { return; } diff --git a/files/redaxo-main/redaxo/src/addons/structure/pages/index.php.txt b/files/redaxo-main/redaxo/src/addons/structure/pages/index.php.txt index ad911e6f1..a94e36201 100644 --- a/files/redaxo-main/redaxo/src/addons/structure/pages/index.php.txt +++ b/files/redaxo-main/redaxo/src/addons/structure/pages/index.php.txt @@ -322,13 +322,14 @@ if ($addon->getPlugin('content')->isAvailable()) { if ($structureContext->getCategoryId() > 0 || (0 == $structureContext->getCategoryId() && !$user->getComplexPerm('structure')->hasMountpoints())) { $tmplHead = ''; + $templateNames = []; if ($templateSelect) { $templateSelect->setName('template_id'); $templateSelect->setSize(1); $templateSelect->setStyle('class="form-control selectpicker"'); - $tEMPLATENAME = $templateSelect->getTemplates(); - $tEMPLATENAME[0] = rex_i18n::msg('template_default_name'); + $templateNames = $templateSelect->getTemplates(); + $templateNames[0] = rex_i18n::msg('template_default_name'); $tmplHead = '' . rex_i18n::msg('header_template') . ''; } @@ -528,7 +529,7 @@ if ($structureContext->getCategoryId() > 0 || (0 == $structureContext->getCatego $tmplTd = ''; if ($templateSelect) { - $tmpl = $tEMPLATENAME[(int) $sql->getValue('template_id')] ?? ''; + $tmpl = rex_escape($templateNames[(int) $sql->getValue('template_id')] ?? ''); $tmplTd = '
' . $tmpl . '
'; } @@ -553,8 +554,9 @@ if ($structureContext->getCategoryId() > 0 || (0 == $structureContext->getCatego $tmplTd = ''; if ($templateSelect) { - $tmpl = $tEMPLATENAME[$sql->getValue('template_id')] ?? ''; - $tmplTd = '' . $tmpl . ''; + $tmpl = rex_escape($templateNames[(int) $sql->getValue('template_id')] ?? ''); + $tmplTd = ' +
' . $tmpl . '
'; } $echo .= ' diff --git a/files/redaxo-main/redaxo/src/addons/structure/plugins/history/boot.php.txt b/files/redaxo-main/redaxo/src/addons/structure/plugins/history/boot.php.txt index 8f597eff6..bbe8b8612 100644 --- a/files/redaxo-main/redaxo/src/addons/structure/plugins/history/boot.php.txt +++ b/files/redaxo-main/redaxo/src/addons/structure/plugins/history/boot.php.txt @@ -37,11 +37,11 @@ if ('' != $historyDate) { } if (!$user) { - throw new rex_exception('no permission'); + throw new rex_http_exception(new rex_exception('no permission'), rex_response::HTTP_UNAUTHORIZED); } if (!$user->hasPerm('history[article_rollback]')) { - throw new rex_exception('no permission for the slice version'); + throw new rex_http_exception(new rex_exception('no permission for the slice version'), rex_response::HTTP_FORBIDDEN); } rex_extension::register('ART_INIT', static function (rex_extension_point $ep) { diff --git a/files/redaxo-main/redaxo/src/core/backend.php.txt b/files/redaxo-main/redaxo/src/core/backend.php.txt index 1194d40a3..5fc8246d0 100644 --- a/files/redaxo-main/redaxo/src/core/backend.php.txt +++ b/files/redaxo-main/redaxo/src/core/backend.php.txt @@ -8,7 +8,7 @@ header("Content-Security-Policy: frame-ancestors 'self'"); // as we assume their url will change when the underlying content changes if (rex_get('asset') && rex_get('buster')) { /** @psalm-taint-escape file */ // it is not escaped here, but it is validated below via the realpath - $assetFile = rex_get('asset'); + $assetFile = rex_get('asset', 'string'); // relative to the assets-root if (str_starts_with($assetFile, '/assets/')) { @@ -18,34 +18,39 @@ if (rex_get('asset') && rex_get('buster')) { $fullPath = realpath($assetFile); $assetDir = rex_path::assets(); + if (!$fullPath) { + throw new rex_http_exception(new Exception('File "' . $assetFile . '" not found'), rex_response::HTTP_NOT_FOUND); + } if (!str_starts_with($fullPath, $assetDir)) { - throw new Exception('Assets can only be streamed from within the assets folder. "' . $fullPath . '" is not within "' . $assetDir . '"'); + throw new rex_http_exception(new Exception('Assets can only be streamed from within the assets folder. "' . $fullPath . '" is not within "' . $assetDir . '"'), rex_response::HTTP_NOT_FOUND); } $ext = rex_file::extension($assetFile); - if ('js' === $ext) { - $js = rex_file::require($assetFile); + if (!in_array($ext, ['js', 'css'], true)) { + throw new rex_http_exception(new Exception('Only JS and CSS files can be streamed from the assets folder'), rex_response::HTTP_NOT_FOUND); + } - $js = preg_replace('@^//# sourceMappingURL=.*$@m', '', $js); + $content = rex_file::get($assetFile); + if (null === $content) { + throw new rex_http_exception(new Exception('File "' . $assetFile . '" not found'), rex_response::HTTP_NOT_FOUND); + } + + if ('js' === $ext) { + $js = preg_replace('@^//# sourceMappingURL=.*$@m', '', $content); rex_response::sendCacheControl('max-age=31536000, immutable'); rex_response::sendContent($js, 'application/javascript'); - } elseif ('css' === $ext) { - $styles = rex_file::require($assetFile); - + } else { // If we are in a directory off the root, add a relative path here back to the root, like "../" // get the public path to this file, plus the baseurl $relativeroot = ''; $pubroot = dirname($_SERVER['PHP_SELF']) . '/' . $relativeroot; $prefix = $pubroot . dirname($assetFile) . '/'; - $styles = preg_replace('/(url\(["\']?)([^\/"\'])([^\:\)]+["\']?\))/i', '$1' . $prefix . '$2$3', $styles); + $styles = preg_replace('/(url\(["\']?)([^\/"\'])([^\:\)]+["\']?\))/i', '$1' . $prefix . '$2$3', $content); rex_response::sendCacheControl('max-age=31536000, immutable'); rex_response::sendContent($styles, 'text/css'); - } else { - rex_response::setStatus(rex_response::HTTP_NOT_FOUND); - rex_response::sendContent('file not found'); } exit; } diff --git a/files/redaxo-main/redaxo/src/core/install.php.txt b/files/redaxo-main/redaxo/src/core/install.php.txt index b50f99ce4..eceec6e94 100644 --- a/files/redaxo-main/redaxo/src/core/install.php.txt +++ b/files/redaxo-main/redaxo/src/core/install.php.txt @@ -59,6 +59,13 @@ if (!$hasPasswordChanged) { ->update(); } +// The foreign key references a varchar column (passkey id). +// We always remove the foreign key here, so that it is possible to change the charset of passkey id column. +$sessionTable = rex_sql_table::get(rex::getTable('user_session')); +if ($sessionTable->exists()) { + $sessionTable->removeForeignKey(rex::getTable('user_session') . '_passkey_id')->alter(); +} + rex_sql_table::get(rex::getTable('user_passkey')) ->ensureColumn(new rex_sql_column('id', 'varchar(255)')) ->ensureColumn(new rex_sql_column('user_id', 'int(10) unsigned')) @@ -68,7 +75,7 @@ rex_sql_table::get(rex::getTable('user_passkey')) ->ensureForeignKey(new rex_sql_foreign_key(rex::getTable('user_passkey') . '_user_id', rex::getTable('user'), ['user_id' => 'id'], rex_sql_foreign_key::CASCADE, rex_sql_foreign_key::CASCADE)) ->ensure(); -rex_sql_table::get(rex::getTable('user_session')) +$sessionTable ->ensureColumn(new rex_sql_column('session_id', 'varchar(255)')) ->ensureColumn(new rex_sql_column('user_id', 'int(10) unsigned')) ->ensureColumn(new rex_sql_column('cookie_key', 'varchar(255)', true)) diff --git a/files/redaxo-main/redaxo/src/core/lib/api_function.php.txt b/files/redaxo-main/redaxo/src/core/lib/api_function.php.txt index 3d4a7a2ee..710f78c7c 100644 --- a/files/redaxo-main/redaxo/src/core/lib/api_function.php.txt +++ b/files/redaxo-main/redaxo/src/core/lib/api_function.php.txt @@ -90,9 +90,9 @@ abstract class rex_api_function self::$instance = $apiImpl; return $apiImpl; } - throw new rex_exception('$apiClass is expected to define a subclass of rex_api_function, "' . $apiClass . '" given!'); + throw new rex_http_exception(new rex_exception('$apiClass is expected to define a subclass of rex_api_function, "' . $apiClass . '" given!'), rex_response::HTTP_NOT_FOUND); } - throw new rex_exception('$apiClass "' . $apiClass . '" not found!'); + throw new rex_http_exception(new rex_exception('$apiClass "' . $apiClass . '" not found!'), rex_response::HTTP_NOT_FOUND); } return null; diff --git a/files/redaxo-main/redaxo/src/core/lib/exception.php.txt b/files/redaxo-main/redaxo/src/core/lib/exception.php.txt index 3971dee16..377521337 100644 --- a/files/redaxo-main/redaxo/src/core/lib/exception.php.txt +++ b/files/redaxo-main/redaxo/src/core/lib/exception.php.txt @@ -91,6 +91,11 @@ class rex_http_exception extends rex_exception { return $this->httpCode; } + + public function isClientError(): bool + { + return str_starts_with($this->httpCode, '4'); + } } /** diff --git a/files/redaxo-main/redaxo/src/core/lib/login/backend_login.php.txt b/files/redaxo-main/redaxo/src/core/lib/login/backend_login.php.txt index 09549ef5a..6e2de7ed3 100644 --- a/files/redaxo-main/redaxo/src/core/lib/login/backend_login.php.txt +++ b/files/redaxo-main/redaxo/src/core/lib/login/backend_login.php.txt @@ -132,11 +132,13 @@ class rex_backend_login extends rex_login $add = ''; if (($password = $this->user->getValue('password')) && self::passwordNeedsRehash($password)) { $add .= 'password = ?, '; - $params[] = self::passwordHash($this->userPassword, true); + $params[] = $password = self::passwordHash($this->userPassword, true); } array_push($params, rex_sql::datetime(), rex_sql::datetime(), session_id(), $this->userLogin); $sql->setQuery('UPDATE ' . $this->tableName . ' SET ' . $add . 'login_tries=0, lasttrydate=?, lastlogin=?, session_id=? WHERE login=? LIMIT 1', $params); + $this->setSessionVar(self::SESSION_PASSWORD, $password); + if ($this->stayLoggedIn || $loggedInViaCookie) { if (!$cookiekey || !$loggedInViaCookie) { $cookiekey = base64_encode(random_bytes(64)); diff --git a/files/redaxo-main/redaxo/src/core/lib/login/login.php.txt b/files/redaxo-main/redaxo/src/core/lib/login/login.php.txt index efe021560..42235621d 100644 --- a/files/redaxo-main/redaxo/src/core/lib/login/login.php.txt +++ b/files/redaxo-main/redaxo/src/core/lib/login/login.php.txt @@ -323,9 +323,10 @@ class rex_login if (!$this->impersonator->getRows()) { $ok = false; $this->message = rex_i18n::msg('login_user_not_found'); - } - $sessionPassword = $this->getSessionVar(self::SESSION_PASSWORD, null); - if (null !== $sessionPassword && $this->impersonator->getValue($this->passwordColumn) !== $sessionPassword) { + } elseif ( + null !== ($sessionPassword = $this->getSessionVar(self::SESSION_PASSWORD, null)) && + $this->impersonator->getValue($this->passwordColumn) !== $sessionPassword + ) { $ok = false; $this->message = rex_i18n::msg('login_session_expired'); } @@ -339,9 +340,11 @@ class rex_login if (!$this->user->getRows()) { $ok = false; $this->message = rex_i18n::msg('login_user_not_found'); - } - $sessionPassword = $this->getSessionVar(self::SESSION_PASSWORD, null); - if (!$this->impersonator && null !== $sessionPassword && (string) $this->user->getValue($this->passwordColumn) !== $sessionPassword) { + } elseif ( + !$this->impersonator && + null !== ($sessionPassword = $this->getSessionVar(self::SESSION_PASSWORD, null)) && + (string) $this->user->getValue($this->passwordColumn) !== $sessionPassword + ) { $ok = false; $this->message = rex_i18n::msg('login_session_expired'); } diff --git a/files/redaxo-main/redaxo/src/core/lib/util/logger.php.txt b/files/redaxo-main/redaxo/src/core/lib/util/logger.php.txt index 39318e76f..75c13c4a2 100644 --- a/files/redaxo-main/redaxo/src/core/lib/util/logger.php.txt +++ b/files/redaxo-main/redaxo/src/core/lib/util/logger.php.txt @@ -37,10 +37,22 @@ class rex_logger extends AbstractLogger { if ($exception instanceof ErrorException) { self::logError($exception->getSeverity(), $exception->getMessage(), $exception->getFile(), $exception->getLine(), $url); - } else { - $logger = self::factory(); - $logger->log($exception::class, $exception->getMessage(), [], $exception->getFile(), $exception->getLine(), $url); + + return; + } + + if ($exception instanceof rex_http_exception) { + // Client errors should not be logged to system error log (if not debug mode or backend admin). + // This prevents that external website visitors can fill up the log (and possibly trigger error emails etc.). + if (!rex::isDebugMode() && $exception->isClientError() && (!($user = rex_backend_login::createUser()) || !$user->isAdmin())) { + return; + } + + $exception = $exception->getPrevious() ?? $exception; // log original exception } + + $logger = self::factory(); + $logger->log($exception::class, $exception->getMessage(), [], $exception->getFile(), $exception->getLine(), $url); } /** diff --git a/js/searchIndex.js b/js/searchIndex.js index 40b3ce578..715e1465c 100644 --- a/js/searchIndex.js +++ b/js/searchIndex.js @@ -5470,6 +5470,11 @@ Search.appendIndex( "name": "getHttpCode", "summary": "", "url": "classes/rex-http-exception.html#method_getHttpCode" + }, { + "fqsen": "\\rex_http_exception\u003A\u003AisClientError\u0028\u0029", + "name": "isClientError", + "summary": "", + "url": "classes/rex-http-exception.html#method_isClientError" }, { "fqsen": "\\rex_yaml_parse_exception", "name": "rex_yaml_parse_exception",