From 13ece3550e4935865a410009e060b4f4b036f949 Mon Sep 17 00:00:00 2001 From: Brian Wolff Date: Mon, 25 Apr 2016 14:08:46 -0400 Subject: [PATCH] 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 Signed-off-by: Chad Horohoe --- 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 6ebbf32ab0..0e06bcee16 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -4261,7 +4261,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 6a869dd45f..b090817de6 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 a3abcad05f..b5e5d806ee 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -1863,11 +1863,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 2f88f925d5..5fffde3547 100644 --- a/tests/parser/parserTests.txt +++ b/tests/parser/parserTests.txt @@ -13159,7 +13159,7 @@ Image with link parameter, wgExternalLinkTarget !! config wgExternalLinkTarget='foobar' !! html/php -

Foobar.jpg +

Foobar.jpg

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

Title +

Title

!! end -- 2.20.1