Page MenuHomePhabricator

0001-Do-not-directly-redirect-to-interwikis-but-use-splas.patch

Authored By
matmarex
Aug 5 2016, 2:40 PM
Size
14 KB
Referenced Files
None
Subscribers
None

0001-Do-not-directly-redirect-to-interwikis-but-use-splas.patch

From 0066c796ca1c5a13b7f3a76c1da4927f764610a8 Mon Sep 17 00:00:00 2001
From: Brian Wolff <bawolff+wn@gmail.com>
Date: Sun, 7 Feb 2016 08:07:20 -0500
Subject: [PATCH] Do not directly redirect to interwikis, but use splash page
Directly redirecting based on a url paramter might potentially
be used in a phishing attack to confuse users.
Bug: T109140
Bug: T122209
Change-Id: I6c604439320fa876719933cc7f3a3ff04fb1a6ad
---
autoload.php | 1 +
includes/OutputPage.php | 4 +-
includes/Title.php | 27 ++++++++
includes/specialpage/RedirectSpecialPage.php | 2 +-
includes/specialpage/SpecialPageFactory.php | 1 +
includes/specials/SpecialChangeCredentials.php | 2 +-
includes/specials/SpecialChangeEmail.php | 2 +-
includes/specials/SpecialGoToInterwiki.php | 79 ++++++++++++++++++++++
includes/specials/SpecialPageLanguage.php | 2 +-
includes/specials/SpecialPreferences.php | 2 +-
includes/specials/SpecialSearch.php | 2 +-
includes/specials/helpers/LoginHelper.php | 2 +-
.../specials/pre-authmanager/SpecialUserlogin.php | 2 +-
languages/i18n/en.json | 5 +-
languages/i18n/qqq.json | 5 +-
languages/messages/MessagesEn.php | 1 +
16 files changed, 128 insertions(+), 11 deletions(-)
create mode 100644 includes/specials/SpecialGoToInterwiki.php
diff --git a/autoload.php b/autoload.php
index 29f986e..4996e6c 100644
--- a/autoload.php
+++ b/autoload.php
@@ -1295,6 +1295,7 @@ $wgAutoloadLocalClasses = [
'SpecialExpandTemplates' => __DIR__ . '/includes/specials/SpecialExpandTemplates.php',
'SpecialExport' => __DIR__ . '/includes/specials/SpecialExport.php',
'SpecialFilepath' => __DIR__ . '/includes/specials/SpecialFilepath.php',
+ 'SpecialGoToInterwiki' => __DIR__ . '/includes/specials/SpecialGoToInterwiki.php',
'SpecialImport' => __DIR__ . '/includes/specials/SpecialImport.php',
'SpecialJavaScriptTest' => __DIR__ . '/includes/specials/SpecialJavaScriptTest.php',
'SpecialLinkAccounts' => __DIR__ . '/includes/specials/SpecialLinkAccounts.php',
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index b6c48ab..d3bf747 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -2604,7 +2604,9 @@ class OutputPage extends ContextSource {
} else {
$titleObj = Title::newFromText( $returnto );
}
- if ( !is_object( $titleObj ) ) {
+ // We don't want people to return to external interwiki. That
+ // might potentially be used as part of a phishing scheme
+ if ( !is_object( $titleObj ) || $titleObj->isExternal() ) {
$titleObj = Title::newMainPage();
}
diff --git a/includes/Title.php b/includes/Title.php
index ea42768..1fb1d1e 100644
--- a/includes/Title.php
+++ b/includes/Title.php
@@ -1663,6 +1663,33 @@ class Title implements LinkTarget {
}
/**
+ * Get a url appropriate for making redirects based on an untrusted url arg
+ *
+ * This is basically the same as getFullUrl(), but in the case of external
+ * interwikis, we send the user to a landing page, to prevent possible
+ * phishing attacks and the like.
+ *
+ * @note Uses current protocol by default, since technically relative urls
+ * aren't allowed in redirects per HTTP spec, so this is not suitable for
+ * places where the url gets cached, as might pollute between
+ * https and non-https users.
+ * @see self::getLocalURL for the arguments.
+ * @param array|string $query
+ * @param string $proto Protocol type to use in URL
+ * @return String. A url suitable to use in an HTTP location header.
+ */
+ public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) {
+ $target = $this;
+ if ( $this->isExternal() ) {
+ $target = SpecialPage::getTitleFor(
+ 'GoToInterwiki',
+ $this->getPrefixedDBKey()
+ );
+ }
+ return $target->getFullUrl( $query, false, $proto );
+ }
+
+ /**
* Get a URL with no fragment or server name (relative URL) from a Title object.
* If this page is generated with action=render, however,
* $wgServer is prepended to make an absolute URL.
diff --git a/includes/specialpage/RedirectSpecialPage.php b/includes/specialpage/RedirectSpecialPage.php
index ea7d783..01787d3 100644
--- a/includes/specialpage/RedirectSpecialPage.php
+++ b/includes/specialpage/RedirectSpecialPage.php
@@ -41,7 +41,7 @@ abstract class RedirectSpecialPage extends UnlistedSpecialPage {
$query = $this->getRedirectQuery();
// Redirect to a page title with possible query parameters
if ( $redirect instanceof Title ) {
- $url = $redirect->getFullURL( $query );
+ $url = $redirect->getFullUrlForRedirect( $query );
$this->getOutput()->redirect( $url );
return $redirect;
diff --git a/includes/specialpage/SpecialPageFactory.php b/includes/specialpage/SpecialPageFactory.php
index b69b28a..7773f73 100644
--- a/includes/specialpage/SpecialPageFactory.php
+++ b/includes/specialpage/SpecialPageFactory.php
@@ -144,6 +144,7 @@ class SpecialPageFactory {
'RandomInCategory' => 'SpecialRandomInCategory',
'Randomredirect' => 'SpecialRandomredirect',
'Randomrootpage' => 'SpecialRandomrootpage',
+ 'GoToInterwiki' => 'SpecialGoToInterwiki',
// High use pages
'Mostlinkedcategories' => 'MostlinkedCategoriesPage',
diff --git a/includes/specials/SpecialChangeCredentials.php b/includes/specials/SpecialChangeCredentials.php
index 4da8049..55bbb9b 100644
--- a/includes/specials/SpecialChangeCredentials.php
+++ b/includes/specials/SpecialChangeCredentials.php
@@ -233,7 +233,7 @@ class SpecialChangeCredentials extends AuthManagerSpecialPage {
}
$title = Title::newFromText( $returnTo );
- return $title->getFullURL( $returnToQuery );
+ return $title->getFullUrlForRedirect( $returnToQuery );
}
protected function getRequestBlacklist() {
diff --git a/includes/specials/SpecialChangeEmail.php b/includes/specials/SpecialChangeEmail.php
index 785447f..eb98fe7 100644
--- a/includes/specials/SpecialChangeEmail.php
+++ b/includes/specials/SpecialChangeEmail.php
@@ -136,7 +136,7 @@ class SpecialChangeEmail extends FormSpecialPage {
$query = $request->getVal( 'returntoquery' );
if ( $this->status->value === true ) {
- $this->getOutput()->redirect( $titleObj->getFullURL( $query ) );
+ $this->getOutput()->redirect( $titleObj->getFullUrlForRedirect( $query ) );
} elseif ( $this->status->value === 'eauth' ) {
# Notify user that a confirmation email has been sent...
$this->getOutput()->wrapWikiMsg( "<div class='error' style='clear: both;'>\n$1\n</div>",
diff --git a/includes/specials/SpecialGoToInterwiki.php b/includes/specials/SpecialGoToInterwiki.php
new file mode 100644
index 0000000..809a14a
--- /dev/null
+++ b/includes/specials/SpecialGoToInterwiki.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Implements Special:GoToInterwiki
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Landing page for non-local interwiki links.
+ *
+ * Meant to warn people that the site they're visiting
+ * is not the local wiki (In case of phishing tricks).
+ * Only meant to be used for things that directly
+ * redirect from url (e.g. Special:Search/google:foo )
+ * Not meant for general interwiki linking (e.g.
+ * [[google:foo]] should still directly link)
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialGoToInterwiki extends UnlistedSpecialPage {
+ public function __construct( $name = 'GoToInterwiki' ) {
+ parent::__construct( $name );
+ }
+
+ public function execute( $par ) {
+ $this->setHeaders();
+ $target = Title::newFromText( $par );
+ // Disallow special pages as a precaution against
+ // possible redirect loops.
+ if ( !$target || $target->isSpecialPage() ) {
+ $this->getOutput()->setStatusCode( 404 );
+ $this->getOutput()->addWikiMsg( 'gotointerwiki-invalid' );
+ return;
+ }
+
+ $url = $target->getFullURL();
+ if ( !$target->isExternal() || $target->isLocal() ) {
+ // Either a normal page, or a local interwiki.
+ // just redirect.
+ $this->getOutput()->redirect( $url, '301' );
+ } else {
+ $this->getOutput()->addWikiMsg(
+ 'gotointerwiki-external',
+ $url,
+ $target->getFullText()
+ );
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ public function requiresWrite() {
+ return false;
+ }
+
+ /**
+ * @return String
+ */
+ protected function getGroupName() {
+ return 'redirects';
+ }
+}
diff --git a/includes/specials/SpecialPageLanguage.php b/includes/specials/SpecialPageLanguage.php
index 5322a04..0035829 100644
--- a/includes/specials/SpecialPageLanguage.php
+++ b/includes/specials/SpecialPageLanguage.php
@@ -141,7 +141,7 @@ class SpecialPageLanguage extends FormSpecialPage {
);
// Url to redirect to after the operation
- $this->goToUrl = $title->getFullURL();
+ $this->goToUrl = $title->getFullUrlForRedirect();
// Check if user wants to use default language
if ( $data['selectoptions'] == 1 ) {
diff --git a/includes/specials/SpecialPreferences.php b/includes/specials/SpecialPreferences.php
index f00477f..8366c22 100644
--- a/includes/specials/SpecialPreferences.php
+++ b/includes/specials/SpecialPreferences.php
@@ -148,7 +148,7 @@ class SpecialPreferences extends SpecialPage {
// Set session data for the success message
$this->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
- $url = $this->getPageTitle()->getFullURL();
+ $url = $this->getPageTitle()->getFullUrlForRedirect();
$this->getOutput()->redirect( $url );
return true;
diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php
index 9690d45..2387ec0 100644
--- a/includes/specials/SpecialSearch.php
+++ b/includes/specials/SpecialSearch.php
@@ -218,7 +218,7 @@ class SpecialSearch extends SpecialPage {
Hooks::run( 'SpecialSearchGoResult', [ $term, $title, &$url ] )
) {
if ( $url === null ) {
- $url = $title->getFullURL();
+ $url = $title->getFullUrlForRedirect();
}
$this->getOutput()->redirect( $url );
diff --git a/includes/specials/helpers/LoginHelper.php b/includes/specials/helpers/LoginHelper.php
index f853f41..37aebc2 100644
--- a/includes/specials/helpers/LoginHelper.php
+++ b/includes/specials/helpers/LoginHelper.php
@@ -89,7 +89,7 @@ class LoginHelper extends ContextSource {
}
if ( $type === 'successredirect' ) {
- $redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto );
+ $redirectUrl = $returnToTitle->getFullUrlForRedirect( $returnToQuery, false, $proto );
$this->getOutput()->redirect( $redirectUrl );
} else {
$this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options );
diff --git a/includes/specials/pre-authmanager/SpecialUserlogin.php b/includes/specials/pre-authmanager/SpecialUserlogin.php
index 09132f2..7a57dbc 100644
--- a/includes/specials/pre-authmanager/SpecialUserlogin.php
+++ b/includes/specials/pre-authmanager/SpecialUserlogin.php
@@ -1422,7 +1422,7 @@ class LoginFormPreAuthManager extends SpecialPage {
}
if ( $type == 'successredirect' ) {
- $redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto );
+ $redirectUrl = $returnToTitle->getFullUrlForRedirect( $returnToQuery, $proto );
$this->getOutput()->redirect( $redirectUrl );
} else {
$this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options );
diff --git a/languages/i18n/en.json b/languages/i18n/en.json
index bc7d78d..5d7407c 100644
--- a/languages/i18n/en.json
+++ b/languages/i18n/en.json
@@ -4193,5 +4193,8 @@
"linkaccounts-submit": "Link accounts",
"unlinkaccounts": "Unlink accounts",
"unlinkaccounts-success": "The account was unlinked.",
- "authenticationdatachange-ignored": "The authentication data change was not handled. Maybe no provider was configured?"
+ "authenticationdatachange-ignored": "The authentication data change was not handled. Maybe no provider was configured?",
+ "gotointerwiki": "Leaving {{SITENAME}}",
+ "gotointerwiki-invalid": "The specified title was invalid.",
+ "gotointerwiki-external": "You are about to leave {{SITENAME}} to visit [[$2]] which is a separate website.\n\n[$1 Click here to continue on to $1]."
}
diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json
index b553756..fb27a57 100644
--- a/languages/i18n/qqq.json
+++ b/languages/i18n/qqq.json
@@ -4375,5 +4375,8 @@
"linkaccounts-submit": "Text of the main submit button on [[Special:LinkAccounts]] (when there is one)",
"unlinkaccounts": "Title of the special page [[Special:UnlinkAccounts]] which allows the user to remove linked remote accounts.",
"unlinkaccounts-success": "Account unlinking form success message",
- "authenticationdatachange-ignored": "Shown when authentication data change was unsuccessful due to configuration problems.\n\nCf. e.g. {{msg-mw|Passwordreset-ignored}}."
+ "authenticationdatachange-ignored": "Shown when authentication data change was unsuccessful due to configuration problems.\n\nCf. e.g. {{msg-mw|Passwordreset-ignored}}.",
+ "gotointerwiki": "{{doc-special|GoToInterwiki}}\n\nSpecial:GoToInterwiki is a warning page displayed before redirecting users to external interwiki links. Its triggered by people going to something like [[Special:Search/google:foo]].",
+ "gotointerwiki-invalid": "Message shown on Special:GoToInterwiki if given an invalid title.",
+ "gotointerwiki-external": "Message shown on Special:GoToInterwiki if given a external interwiki link (e.g. [[Special:GoToInterwiki/Google:Foo]]). $1 is the full url the user is trying to get to. $2 is the text of the interwiki link (e.g.\ "Google:foo\")."
}
diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php
index 589144c..6ae2db8 100644
--- a/languages/messages/MessagesEn.php
+++ b/languages/messages/MessagesEn.php
@@ -426,6 +426,7 @@ $specialPageAliases = [
'Fewestrevisions' => [ 'FewestRevisions' ],
'FileDuplicateSearch' => [ 'FileDuplicateSearch' ],
'Filepath' => [ 'FilePath' ],
+ 'GoToInterwiki' => [ 'GoToInterwiki' ],
'Import' => [ 'Import' ],
'Invalidateemail' => [ 'InvalidateEmail' ],
'JavaScriptTest' => [ 'JavaScriptTest' ],
--
2.8.1.windows.1

File Metadata

Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3904006
Default Alt Text
0001-Do-not-directly-redirect-to-interwikis-but-use-splas.patch (14 KB)

Event Timeline