From 2f17bced413fed5bb732c40193f7fd5be464fa99 Mon Sep 17 00:00:00 2001 From: Brian Wolff Date: Mon, 25 Apr 2016 14:08:46 -0400 Subject: [PATCH] [SECURITY] Add rel="noreferrer noopener" when target attribute would open window noreferrer is used as support for noopener is very limited. This is to prevent the attack detailed at https://mathiasbynens.github.io/rel-noopener/ where you can navigate the parent window, even if the new window is a cross-origin. Bug: T133507 Change-Id: I6e4ab938861e246ff44048077b94847e303f1859 --- includes/DefaultSettings.php | 8 +++++++- includes/Linker.php | 11 ++++++++++- includes/parser/Parser.php | 19 +++++++++++++++---- tests/parser/parserTests.txt | 4 ++-- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index d65e0ad..1bbdbe7 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -4221,7 +4221,13 @@ $wgDebugTidy = false; $wgRawHtml = false; /** - * Set a default target for external links, e.g. _blank to pop up a new window + * Set a default target for external links, e.g. _blank to pop up a new window. + * + * This will also set the "noreferrer" and "noopener" link rel to prevent the + * attack described at https://mathiasbynens.github.io/rel-noopener/ . + * Some older browsers may not support these link attributes, hence + * setting $wgExternalLinkTarget to _blank may represent a security risk + * to some of your users. */ $wgExternalLinkTarget = false; diff --git a/includes/Linker.php b/includes/Linker.php index 6a869dd..b090817 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -1084,7 +1084,16 @@ class Linker { if ( !$title ) { $title = $wgTitle; } - $attribs['rel'] = Parser::getExternalLinkRel( $url, $title ); + $newRel = Parser::getExternalLinkRel( $url, $title ); + if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) { + $attribs['rel'] = $newRel; + } elseif( $newRel !== '' ) { + // Merge the rel attributes. + $newRels = explode( ' ', $newRel ); + $oldRels = explode( ' ', $attribs['rel'] ); + $combined = array_unique( array_merge( $newRels, $oldRels ) ); + $attribs['rel'] = implode( ' ', $combined ); + } $link = ''; $success = Hooks::run( 'LinkerMakeExternalLink', [ &$url, &$text, &$link, &$attribs, $linktype ] ); diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index a1d62e5..891b8d2 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -1872,11 +1872,22 @@ class Parser { */ public function getExternalLinkAttribs( $url = false ) { $attribs = []; - $attribs['rel'] = self::getExternalLinkRel( $url, $this->mTitle ); - - if ( $this->mOptions->getExternalLinkTarget() ) { - $attribs['target'] = $this->mOptions->getExternalLinkTarget(); + $rel = self::getExternalLinkRel( $url, $this->mTitle ); + + $target = $this->mOptions->getExternalLinkTarget(); + if ( $target ) { + $attribs['target'] = $target; + if ( !in_array( $target, [ '_self', '_parent', '_top' ] ) ) { + // T133507. New windows can navigate parent cross-origin. + // Including noreferrer due to lacking browser + // support of noopener. Eventually noreferrer should be removed. + if ( $rel !== '' ) { + $rel .= ' '; + } + $rel .= 'noreferrer noopener'; + } } + $attribs['rel'] = $rel; return $attribs; } diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt index 23bdbde..b3aca74 100644 --- a/tests/parser/parserTests.txt +++ b/tests/parser/parserTests.txt @@ -13132,7 +13132,7 @@ Image with link parameter, wgExternalLinkTarget !! config wgExternalLinkTarget='foobar' !! html/php -

Foobar.jpg +

Foobar.jpg

!! end @@ -13166,7 +13166,7 @@ Image with link parameter, wgExternalLinkTarget, unnamed parameter !! config wgExternalLinkTarget='foobar' !! html/php -

Title +

Title

!! end -- 2.0.1