VYPR
Medium severityNVD Advisory· Published Jun 9, 2026· Updated Jun 9, 2026

CVE-2026-47347

CVE-2026-47347

Description

Applications that use GeneralUtility::sanitizeLocalUrl to allow only local URLs are vulnerable to open redirect attacks if the URL is used after it has passed the aforementioned sanitization checks. This enables attackers to redirect users to external content and carry out phishing attacks. This issue affects TYPO3 CMS versions before 10.4.57, 11.0.0-11.5.50, 12.0.0-12.4.45, 13.0.0-13.4.30 and 14.0.0-14.3.2.

Affected products

2
  • TYPO3/Typo3references
  • TYPO3/TYPO3 CMSllm-fuzzy
    Range: <10.4.57, 11.0.0-11.5.50, 12.0.0-12.4.45, 13.0.0-13.4.30, 14.0.0-14.3.2

Patches

2
3ffc0835012c

[SECURITY] Fix open redirection in GeneralUtility::sanitizeLocalUrl

https://github.com/TYPO3/typo3Benjamin FranzkeJun 9, 2026via github-commit-search
2 files changed · +60 8
  • typo3/sysext/core/Classes/Utility/GeneralUtility.php+22 2 modified
    @@ -2127,8 +2127,28 @@ public static function sanitizeLocalUrl(string $url, ServerRequestInterface $req
         {
             $sanitizedUrl = '';
             if (!empty($url)) {
    -            if (strpbrk($url, "\n\r\x00") !== false) {
    -                static::getLogger()->notice('URL "{url}" contains unexpected whitespace and was denied as local url.', ['url' => $url]);
    +            $validUrlCharacters = [
    +                // Percent-Encoding: https://datatracker.ietf.org/doc/html/rfc3986#section-2.1
    +                '%',
    +
    +                // Reserved Characters: https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
    +                // gen-delims
    +                ':', '/', '?', '#', '[', ']', '@',
    +                // sub-delims
    +                '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=',
    +
    +                // Unreserved Characters: https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
    +                '-', '.', '_', '~',
    +                // ALPHA
    +                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    +                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    +                // DIGIT
    +                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    +            ];
    +
    +            $hasInvalidCharacters = str_replace($validUrlCharacters, '', $url) !== '';
    +            if ($hasInvalidCharacters) {
    +                static::getLogger()->notice('The URL "{url}" contains unexpected characters and was denied as local url.', ['url' => $url]);
                     return '';
                 }
     
    
  • typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php+38 6 modified
    @@ -1365,18 +1365,28 @@ public static function sanitizeLocalUrlValidUrlsDataProvider(): array
                     'localhost',
                     '/cms/',
                 ],
    -            '/cms/typo3/alt_intro.php&param=oneparam' => [
    -                '/cms/typo3/alt_intro.php&param=oneparam',
    +            '/cms/foo/%3Fbar?baz' => [
    +                '/cms/foo/%3Fbar?baz',
                     'localhost',
                     '/cms/',
                 ],
    -            '/cms/typo3/alt_intro.php&param=oneparam with spaces' => [
    -                '/cms/typo3/alt_intro.php&param=oneparam with spaces',
    +            '/cms/typo3/alt_intro.php?param=oneparam' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam',
                     'localhost',
                     '/cms/',
                 ],
    -            '/cms/typo3/alt_intro.php&param=oneparam with spaces&normalparam=2' => [
    -                '/cms/typo3/alt_intro.php&param=oneparam with spaces',
    +            '/cms/typo3/alt_intro.php?param=oneparam+with+spaces' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam+with+spaces',
    +                'localhost',
    +                '/cms/',
    +            ],
    +            '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces',
    +                'localhost',
    +                '/cms/',
    +            ],
    +            '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces&normalparam=2' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces',
                     'localhost',
                     '/cms/',
                 ],
    @@ -1440,7 +1450,28 @@ public static function sanitizeLocalUrlInvalidDataProvider(): array
                 'empty string' => [''],
                 'http domain' => ['http://www.google.de/'],
                 'https domain' => ['https://www.google.de/'],
    +            'https domain with' => ['https://www.google.de/'],
    +            'https domain with escape at start' => ['https:\\//www.google.de'],
    +            'https domain with escape in between' => ['https:/\\/www.google.de'],
    +            'https domain with escape after' => ['https://\\www.google.de'],
    +            'https domain with escape instead of slash' => ['https:/\\www.google.de'],
    +            'https domain with double backslash' => ['https:\\\\www.google.de'],
    +            'https domain with double backslash and one slash' => ['https:\\/www.google.de'],
    +            'https domain with quad slash' => ['https:////www.google.de'],
    +            'https domain with newline' => ["htt\nps://www.google.de"],
                 'domain without schema' => ['//www.google.de/'],
    +            'domain without schema escape at start' => ['\\//www.google.de', true],
    +            'domain without schema escape in between' => ['/\\/www.google.de', true],
    +            'domain without schema escape after' => ['//\\www.google.de', true],
    +            'domain without schema escape instead of slash' => ['/\\www.google.de', true],
    +            'domain without schema with double backslash' => ['\\\\www.google.de', true],
    +            'domain without schema with double backslash and one slash' => ['\\/www.google.de', true],
    +            'domain without schema with quad slash' => ['////www.google.de'],
    +            'domain without schema with newline' => ["/\n/www.google.de", true],
    +            'domain without schema with EOT' => ["\x04//google.de", true],
    +            'domain without schema with bell' => ["\x07//google.de", true],
    +            'domain without schema with backspace' => ["\x08//google.de", true],
    +            'domain without schema with form feed' => ["\x0c//google.de", true],
                 'XSS attempt' => ['" onmouseover="alert(123)"'],
                 'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
                 'invalid URL, HTML break out attempt' => ['" >blabuubb'],
    @@ -1450,6 +1481,7 @@ public static function sanitizeLocalUrlInvalidDataProvider(): array
                 'relative URL with location header injection attempt (not known to work) via vertical white space' => ["\v" . '//evil.site/'],
                 'HTTP header smuggling attempt' => ["/\r\nX-Injected: evil", true],
                 'null-byte break out attempt' => ["http\x00://www.google.de"],
    +            'path invalid because it contains unencoded spaces' => ['/cms/typo3/alt_intro.php&param=oneparam with spaces', true],
             ];
         }
     
    
22c2dd5398eb

[SECURITY] Fix open redirection in GeneralUtility::sanitizeLocalUrl

https://github.com/TYPO3/typo3Benjamin FranzkeJun 9, 2026via nvd-ref
2 files changed · +60 8
  • typo3/sysext/core/Classes/Utility/GeneralUtility.php+22 2 modified
    @@ -2595,8 +2595,28 @@ public static function sanitizeLocalUrl(string $url): string
         {
             $sanitizedUrl = '';
             if (!empty($url)) {
    -            if (strpbrk($url, "\n\r\x00") !== false) {
    -                static::getLogger()->notice('URL "{url}" contains unexpected whitespace and was denied as local url.', ['url' => $url]);
    +            $validUrlCharacters = [
    +                // Percent-Encoding: https://datatracker.ietf.org/doc/html/rfc3986#section-2.1
    +                '%',
    +
    +                // Reserved Characters: https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
    +                // gen-delims
    +                ':', '/', '?', '#', '[', ']', '@',
    +                // sub-delims
    +                '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=',
    +
    +                // Unreserved Characters: https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
    +                '-', '.', '_', '~',
    +                // ALPHA
    +                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    +                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    +                // DIGIT
    +                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    +            ];
    +
    +            $hasInvalidCharacters = str_replace($validUrlCharacters, '', $url) !== '';
    +            if ($hasInvalidCharacters) {
    +                static::getLogger()->notice('The URL "{url}" contains unexpected characters and was denied as local url.', ['url' => $url]);
                     return '';
                 }
     
    
  • typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php+38 6 modified
    @@ -1384,18 +1384,28 @@ public static function sanitizeLocalUrlValidUrlsDataProvider(): array
                     'localhost',
                     '/cms/',
                 ],
    -            '/cms/typo3/alt_intro.php&param=oneparam' => [
    -                '/cms/typo3/alt_intro.php&param=oneparam',
    +            '/cms/foo/%3Fbar?baz' => [
    +                '/cms/foo/%3Fbar?baz',
                     'localhost',
                     '/cms/',
                 ],
    -            '/cms/typo3/alt_intro.php&param=oneparam with spaces' => [
    -                '/cms/typo3/alt_intro.php&param=oneparam with spaces',
    +            '/cms/typo3/alt_intro.php?param=oneparam' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam',
                     'localhost',
                     '/cms/',
                 ],
    -            '/cms/typo3/alt_intro.php&param=oneparam with spaces&normalparam=2' => [
    -                '/cms/typo3/alt_intro.php&param=oneparam with spaces',
    +            '/cms/typo3/alt_intro.php?param=oneparam+with+spaces' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam+with+spaces',
    +                'localhost',
    +                '/cms/',
    +            ],
    +            '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces',
    +                'localhost',
    +                '/cms/',
    +            ],
    +            '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces&normalparam=2' => [
    +                '/cms/typo3/alt_intro.php?param=oneparam%20with%20spaces',
                     'localhost',
                     '/cms/',
                 ],
    @@ -1453,7 +1463,28 @@ public static function sanitizeLocalUrlInvalidDataProvider(): array
                 'empty string' => [''],
                 'http domain' => ['http://www.google.de/'],
                 'https domain' => ['https://www.google.de/'],
    +            'https domain with' => ['https://www.google.de/'],
    +            'https domain with escape at start' => ['https:\\//www.google.de'],
    +            'https domain with escape in between' => ['https:/\\/www.google.de'],
    +            'https domain with escape after' => ['https://\\www.google.de'],
    +            'https domain with escape instead of slash' => ['https:/\\www.google.de'],
    +            'https domain with double backslash' => ['https:\\\\www.google.de'],
    +            'https domain with double backslash and one slash' => ['https:\\/www.google.de'],
    +            'https domain with quad slash' => ['https:////www.google.de'],
    +            'https domain with newline' => ["htt\nps://www.google.de"],
                 'domain without schema' => ['//www.google.de/'],
    +            'domain without schema escape at start' => ['\\//www.google.de', true],
    +            'domain without schema escape in between' => ['/\\/www.google.de', true],
    +            'domain without schema escape after' => ['//\\www.google.de', true],
    +            'domain without schema escape instead of slash' => ['/\\www.google.de', true],
    +            'domain without schema with double backslash' => ['\\\\www.google.de', true],
    +            'domain without schema with double backslash and one slash' => ['\\/www.google.de', true],
    +            'domain without schema with quad slash' => ['////www.google.de'],
    +            'domain without schema with newline' => ["/\n/www.google.de", true],
    +            'domain without schema with EOT' => ["\x04//google.de", true],
    +            'domain without schema with bell' => ["\x07//google.de", true],
    +            'domain without schema with backspace' => ["\x08//google.de", true],
    +            'domain without schema with form feed' => ["\x0c//google.de", true],
                 'XSS attempt' => ['" onmouseover="alert(123)"'],
                 'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
                 'invalid URL, HTML break out attempt' => ['" >blabuubb'],
    @@ -1463,6 +1494,7 @@ public static function sanitizeLocalUrlInvalidDataProvider(): array
                 'relative URL with location header injection attempt (not known to work) via vertical white space' => ["\v" . '//evil.site/'],
                 'HTTP header smuggling attempt' => ["/\r\nX-Injected: evil", true],
                 'null-byte break out attempt' => ["http\x00://www.google.de"],
    +            'path invalid because it contains unencoded spaces' => ['/cms/typo3/alt_intro.php&param=oneparam with spaces', true],
             ];
         }
     
    

Vulnerability mechanics

Root cause

"The sanitization function GeneralUtility::sanitizeLocalUrl did not correctly validate all allowed characters in URLs."

Attack vector

An attacker can craft a URL that appears to be a local URL but contains characters that allow it to be interpreted as an external URL. This crafted URL can then be used to redirect users to malicious external content, facilitating phishing attacks. The vulnerability is triggered when the application uses the output of GeneralUtility::sanitizeLocalUrl without further validation [ref_id=1].

Affected code

The vulnerability resides in the `GeneralUtility::sanitizeLocalUrl` function within the TYPO3 CMS core. The function was intended to filter URLs to only allow local resources but failed to account for certain character sequences that could bypass its checks.

What the fix does

The patch modifies the `GeneralUtility::sanitizeLocalUrl` function to more strictly validate the characters allowed within a URL. Previously, it only checked for newline, carriage return, and null bytes. The updated function now checks against a defined set of valid URL characters, including percent-encoded characters, reserved characters, and unreserved characters, preventing the injection of malicious sequences that could lead to open redirects [ref_id=1, patch_id=5349029].

Preconditions

  • inputThe attacker must be able to provide a crafted URL to the application.

Generated on Jun 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

1