From 717544b5880730fbcfaec000563915370c32c4ee Mon Sep 17 00:00:00 2001 From: csteipp Date: Mon, 5 Oct 2015 16:58:42 -0700 Subject: [PATCH] SECURITY: Make Special:MyPage and friends fake redirect to prevent info leak This prevents a malicious person from using external resources on their website to cause the victim's web browser to load Special:MyPage -> User:Username, and then looking it up in the page hit statistics in order to correlate IPs from the malicious person's server log, with usernames on wiki. This feature can be disabled with $wgHideIdentifiableRedirects. Bug: T109724 Change-Id: Ia0e742dc92c77af4832174dfa24c6dcaa6ee80e9 --- includes/DefaultSettings.php | 6 ++++ includes/MediaWiki.php | 44 ++++++++++++++++++++---- includes/specialpage/RedirectSpecialPage.php | 12 +++++++ includes/specials/SpecialMyLanguage.php | 11 ++++++ includes/specials/SpecialMyRedirectPages.php | 50 ++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 6 deletions(-) diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 910c121..e4e77c7 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -4718,6 +4718,12 @@ $wgWhitelistReadRegexp = false; $wgEmailConfirmToEdit = false; /** + * Should MediaWiki attempt to protect user's privacy when doing redirects? + * Keep this true if access counts to articles are made public. + */ +$wgHideIdentifiableRedirects = true; + +/** * Permission keys given to users in each group. * * This is an array where the keys are all groups and each value is an diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php index 676108c..0a9df4b 100644 --- a/includes/MediaWiki.php +++ b/includes/MediaWiki.php @@ -37,6 +37,11 @@ class MediaWiki { private $config; /** + * @var String Cache what action this request is + */ + private $action; + + /** * @param IContextSource|null $context */ public function __construct( IContextSource $context = null ) { @@ -141,13 +146,11 @@ class MediaWiki { * @return string Action */ public function getAction() { - static $action = null; - - if ( $action === null ) { - $action = Action::getActionName( $this->context ); + if ( $this->action === null ) { + $this->action = Action::getActionName( $this->context ); } - return $action; + return $this->action; } /** @@ -241,8 +244,37 @@ class MediaWiki { // Handle any other redirects. // Redirect loops, titleless URL, $wgUsePathInfo URLs, and URLs with a variant } elseif ( !$this->tryNormaliseRedirect( $title ) ) { + // Prevent information leak via Special:MyPage et al (T109724) + if ( $title->isSpecialPage() ) { + $specialPage = SpecialPageFactory::getPage( $title->getDBKey() ); + if ( $specialPage instanceof RedirectSpecialPage + && $this->config->get( 'HideIdentifiableRedirects' ) + && $specialPage->personallyIdentifiableTarget() + ) { + list( , $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBKey() ); + $target = $specialPage->getRedirect( $subpage ); + // target can also be true. We let that case fall through to normal processing. + if ( $target instanceof Title ) { + $query = $specialPage->getRedirectQuery() ?: array(); + $request = new DerivativeRequest( $this->context->getRequest(), $query ); + $request->setRequestURL( $this->context->getRequest()->getRequestURL() ); + $this->context->setRequest( $request ); + // Do not varnish cache these. May vary even for anons + $this->context->getOutput()->lowerCdnMaxage( 0 ); + $this->context->setTitle( $target ); + $wgTitle = $target; + // Reset action type cache. (Special pages have only view) + $this->action = null; + $title = $target; + $output->addJsConfigVars( array( + 'wgInternalRedirectTargetUrl' => $target->getFullURL(), + ) ); + $output->addModules( 'mediawiki.action.view.redirect' ); + } + } + } - // Special pages + // Special pages ($title may have changed since if statement above) if ( NS_SPECIAL == $title->getNamespace() ) { // Actions that need to be made when we have a special pages SpecialPageFactory::executePath( $title, $this->context ); diff --git a/includes/specialpage/RedirectSpecialPage.php b/includes/specialpage/RedirectSpecialPage.php index 9129ee5..5047354 100644 --- a/includes/specialpage/RedirectSpecialPage.php +++ b/includes/specialpage/RedirectSpecialPage.php @@ -94,6 +94,18 @@ abstract class RedirectSpecialPage extends UnlistedSpecialPage { ? $params : false; } + + /** + * Indicate if the target of this redirect can be used to identify + * a particular user of this wiki (e.g., if the redirect is to the + * user page of a User). See T109724. + * + * @since 1.27 + * @return bool + */ + public function personallyIdentifiableTarget() { + return false; + } } /** diff --git a/includes/specials/SpecialMyLanguage.php b/includes/specials/SpecialMyLanguage.php index 3d8ff97..d11fbe6 100644 --- a/includes/specials/SpecialMyLanguage.php +++ b/includes/specials/SpecialMyLanguage.php @@ -99,4 +99,15 @@ class SpecialMyLanguage extends RedirectSpecialArticle { return $base; } } + + /** + * Target can identify a specific user's language preference. + * + * @see T109724 + * @since 1.27 + * @return bool + */ + public function personallyIdentifiableTarget() { + return true; + } } diff --git a/includes/specials/SpecialMyRedirectPages.php b/includes/specials/SpecialMyRedirectPages.php index 5ef03f1..850b1f6 100644 --- a/includes/specials/SpecialMyRedirectPages.php +++ b/includes/specials/SpecialMyRedirectPages.php @@ -45,6 +45,16 @@ class SpecialMypage extends RedirectSpecialArticle { return Title::makeTitle( NS_USER, $this->getUser()->getName() . '/' . $subpage ); } + + /** + * Target identifies a specific User. See T109724. + * + * @since 1.27 + * @return bool + */ + public function personallyIdentifiableTarget() { + return true; + } } /** @@ -68,6 +78,16 @@ class SpecialMytalk extends RedirectSpecialArticle { return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() . '/' . $subpage ); } + + /** + * Target identifies a specific User. See T109724. + * + * @since 1.27 + * @return bool + */ + public function personallyIdentifiableTarget() { + return true; + } } /** @@ -90,6 +110,16 @@ class SpecialMycontributions extends RedirectSpecialPage { public function getRedirect( $subpage ) { return SpecialPage::getTitleFor( 'Contributions', $this->getUser()->getName() ); } + + /** + * Target identifies a specific User. See T109724. + * + * @since 1.27 + * @return bool + */ + public function personallyIdentifiableTarget() { + return true; + } } /** @@ -110,6 +140,16 @@ class SpecialMyuploads extends RedirectSpecialPage { public function getRedirect( $subpage ) { return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() ); } + + /** + * Target identifies a specific User. See T109724. + * + * @since 1.27 + * @return bool + */ + public function personallyIdentifiableTarget() { + return true; + } } /** @@ -132,4 +172,14 @@ class SpecialAllMyUploads extends RedirectSpecialPage { return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() ); } + + /** + * Target identifies a specific User. See T109724. + * + * @since 1.27 + * @return bool + */ + public function personallyIdentifiableTarget() { + return true; + } } -- 2.0.1