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 @@
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
+
+
+
+
+
+
+
+
+
@@ -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",