[SECURITY] Escape the link text in EmailViewHelper 65/49065/2
authorWouter Wolters <typo3@wouterwolters.nl>
Tue, 19 Jul 2016 10:15:53 +0000 (12:15 +0200)
committerOliver Hader <oliver.hader@typo3.org>
Tue, 19 Jul 2016 10:15:55 +0000 (12:15 +0200)
The content of the email link is not escaped correctly.
This leads to XSS in the EmailViewHelper.

Resolves: #76344
Releases: master,7.6,6.2
Security-Commit: a775018a6bfceae85297460c1134c2ec431edbcf
Security-Bulletins: TYPO3-CORE-SA-2016-014, 015, 016, 017, 018
Change-Id: I7f06b1aefc33fc59fdc9d5cb477c1824acf1e07c
Reviewed-on: https://review.typo3.org/49065
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
typo3/sysext/fluid/Classes/ViewHelpers/Link/EmailViewHelper.php
typo3/sysext/fluid/Tests/Unit/ViewHelpers/Link/EmailViewHelperTest.php
typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php

index 7701fc1..a95b2a4 100644 (file)
@@ -61,20 +61,29 @@ class EmailViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedV
      */
     public function render($email)
     {
-        if (TYPO3_MODE === 'FE') {
-            list($linkHref, $linkText) = $GLOBALS['TSFE']->cObj->getMailTo($email, $email);
+        if ($this->isFrontendAvailable()) {
+            list($linkHref, $linkText) = $GLOBALS['TSFE']->cObj->getMailTo($email, '');
+            $escapeSpecialCharacters = !isset($GLOBALS['TSFE']->spamProtectEmailAddresses) || $GLOBALS['TSFE']->spamProtectEmailAddresses !== 'ascii';
         } else {
             $linkHref = 'mailto:' . $email;
-            $linkText = $email;
+            $linkText = htmlspecialchars($email);
+            $escapeSpecialCharacters = true;
         }
         $tagContent = $this->renderChildren();
         if ($tagContent !== null) {
             $linkText = $tagContent;
         }
         $this->tag->setContent($linkText);
-        $escapeSpecialCharacters = !isset($GLOBALS['TSFE']->spamProtectEmailAddresses) || $GLOBALS['TSFE']->spamProtectEmailAddresses !== 'ascii';
         $this->tag->addAttribute('href', $linkHref, $escapeSpecialCharacters);
         $this->tag->forceClosingTag(true);
         return $this->tag->render();
     }
+
+    /**
+     * @return bool
+     */
+    protected function isFrontendAvailable()
+    {
+        return TYPO3_MODE === 'FE';
+    }
 }
index f6a7be6..b1dcf0d 100644 (file)
@@ -13,6 +13,9 @@ namespace TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Link;
  * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General      *
  * Public License for more details.                                       *
  *                                                                        */
+use TYPO3\CMS\Fluid\ViewHelpers\Link\EmailViewHelper;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
 
@@ -66,4 +69,76 @@ class EmailViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\ViewHe
         $this->viewHelper->initialize();
         $this->viewHelper->render('some@email.tld');
     }
+
+    /**
+     * @return array
+     */
+    public function renderEncodesEmailInFrontendDataProvider()
+    {
+        return [
+            'Plain email' => [
+                'some@email.tld',
+                0,
+                '<a href="mailto:some@email.tld">some@email.tld</a>',
+            ],
+            'Plain email with spam protection' => [
+                'some@email.tld',
+                1,
+                '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+tpnfAfnbjm\/ume\');">some(at)email.tld</a>',
+            ],
+            'Plain email with ascii spam protection' => [
+                'some@email.tld',
+                'ascii',
+                '<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#111;&#109;&#101;&#64;&#101;&#109;&#97;&#105;&#108;&#46;&#116;&#108;&#100;">some(at)email.tld</a>',
+            ],
+            'Susceptible email' => [
+                '"><script>alert(\'email\')</script>',
+                0,
+                '<a href="mailto:&quot;&gt;&lt;script&gt;alert(\'email\')&lt;/script&gt;">&quot;&gt;&lt;script&gt;alert(\'email\')&lt;/script&gt;</a>',
+            ],
+            'Susceptible email with spam protection' => [
+                '"><script>alert(\'email\')</script>',
+                1,
+                '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+\u0022\u003E\u003Ctdsjqu\u003Ebmfsu(\u0027fnbjm\u0027)\u003C0tdsjqu\u003E\');">&quot;&gt;&lt;script&gt;alert(\'email\')&lt;/script&gt;</a>',
+            ],
+            'Susceptible email with ascii spam protection' => [
+                '"><script>alert(\'email\')</script>',
+                'ascii',
+                '<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#34;&#62;&#60;&#115;&#99;&#114;&#105;&#112;&#116;&#62;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#101;&#109;&#97;&#105;&#108;&#39;&#41;&#60;&#47;&#115;&#99;&#114;&#105;&#112;&#116;&#62;">&quot;&gt;&lt;script&gt;alert(\'email\')&lt;/script&gt;</a>',
+            ],
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider renderEncodesEmailInFrontendDataProvider
+     * @param string $email
+     * @param string $spamProtectEmailAddresses
+     * @param string $expected
+     */
+    public function renderEncodesEmailInFrontend($email, $spamProtectEmailAddresses, $expected)
+    {
+        /** @var TypoScriptFrontendController $tsfe */
+        $tsfe = $this->getMock(TypoScriptFrontendController::class, ['dummy'], [], '', false);
+        $tsfe->cObj = new ContentObjectRenderer();
+        $tsfe->spamProtectEmailAddresses = $spamProtectEmailAddresses;
+        $tsfe->config = [
+            'config' => [
+                'spamProtectEmailAddresses_atSubst' => '',
+                'spamProtectEmailAddresses_lastDotSubst' => '',
+            ],
+        ];
+        $GLOBALS['TSFE'] = $tsfe;
+        $mockTagBuilder = $this->getMock(\TYPO3\CMS\Fluid\Core\ViewHelper\TagBuilder::class, array('dummy'));
+        $mockTagBuilder->setTagName = 'a';
+        $viewHelper = $this->getMock($this->buildAccessibleProxy(\TYPO3\CMS\Fluid\ViewHelpers\Link\EmailViewHelper::class), array('isFrontendAvailable', 'renderChildren'));
+        $viewHelper->_set('tag', $mockTagBuilder);
+        $viewHelper->expects($this->once())->method('isFrontendAvailable')->willReturn(true);
+        $viewHelper->expects($this->once())->method('renderChildren')->willReturn(null);
+        $viewHelper->initialize();
+        $this->assertSame(
+            $expected,
+            $viewHelper->render($email)
+        );
+    }
 }
index cb96791..025a00d 100644 (file)
@@ -7129,7 +7129,7 @@ class ContentObjectRenderer
     {
         $mailAddress = (string)$mailAddress;
         if ((string)$linktxt === '') {
-            $linktxt = $mailAddress;
+            $linktxt = htmlspecialchars($mailAddress);
         }
 
         $originalMailToUrl = 'mailto:' . $mailAddress;
@@ -7148,7 +7148,7 @@ class ContentObjectRenderer
                 if ($tsfe->config['config']['spamProtectEmailAddresses_atSubst']) {
                     $atLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_atSubst']);
                 }
-                $spamProtectedMailAddress = str_replace('@', $atLabel ? $atLabel : '(at)', $mailAddress);
+                $spamProtectedMailAddress = str_replace('@', $atLabel ? $atLabel : '(at)', htmlspecialchars($mailAddress));
                 if ($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']) {
                     $lastDotLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']);
                     $lastDotLabel = $lastDotLabel ? $lastDotLabel : '(dot)';