r79721 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r79720‎ | r79721 | r79722 >
Date:15:02, 6 January 2011
Author:catrope
Status:ok
Tags:
Comment:
1.17: MFT r79561 (clickjacking fix). SpecialSearch.php and SpecialLinkSearch.php have been refactored after the branch point and needed manual merging
Modified paths:
  • /branches/REL1_17/phase3/includes/Article.php (modified) (history)
  • /branches/REL1_17/phase3/includes/DefaultSettings.php (modified) (history)
  • /branches/REL1_17/phase3/includes/HTMLForm.php (modified) (history)
  • /branches/REL1_17/phase3/includes/HistoryPage.php (modified) (history)
  • /branches/REL1_17/phase3/includes/ImagePage.php (modified) (history)
  • /branches/REL1_17/phase3/includes/OutputPage.php (modified) (history)
  • /branches/REL1_17/phase3/includes/Skin.php (modified) (history)
  • /branches/REL1_17/phase3/includes/diff/DifferenceEngine.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/WebInstallerOutput.php (modified) (history)
  • /branches/REL1_17/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php (modified) (history)
  • /branches/REL1_17/phase3/includes/specials/SpecialAllpages.php (modified) (history)
  • /branches/REL1_17/phase3/includes/specials/SpecialCategories.php (modified) (history)
  • /branches/REL1_17/phase3/includes/specials/SpecialContributions.php (modified) (history)
  • /branches/REL1_17/phase3/includes/specials/SpecialLinkSearch.php (modified) (history)
  • /branches/REL1_17/phase3/includes/specials/SpecialSearch.php (modified) (history)
  • /branches/REL1_17/phase3/includes/specials/SpecialSpecialpages.php (modified) (history)
  • /branches/REL1_17/phase3/includes/specials/SpecialVersion.php (modified) (history)

Diff [purge]

Index: branches/REL1_17/phase3/includes/diff/DifferenceEngine.php
@@ -115,6 +115,8 @@
116116 global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol;
117117 wfProfileIn( __METHOD__ );
118118
 119+ # Allow frames except in certain special cases
 120+ $wgOut->allowClickjacking();
119121
120122 # If external diffs are enabled both globally and for the user,
121123 # we'll use the application/x-external-editor interface to call
@@ -206,6 +208,7 @@
207209 // Check if page is editable
208210 $editable = $this->mNewRev->getTitle()->userCan( 'edit' );
209211 if ( $editable && $this->mNewRev->isCurrent() && $wgUser->isAllowed( 'rollback' ) ) {
 212+ $wgOut->preventClickjacking();
210213 $rollback = '   ' . $sk->generateRollback( $this->mNewRev );
211214 } else {
212215 $rollback = '';
@@ -243,6 +246,7 @@
244247 }
245248 // Build the link
246249 if ( $rcid ) {
 250+ $wgOut->preventClickjacking();
247251 $token = $wgUser->editToken( $rcid );
248252 $patrol = ' <span class="patrollink">[' . $sk->link(
249253 $this->mTitle,
@@ -478,6 +482,7 @@
479483 if ( $this->mRcidMarkPatrolled && $this->mTitle->quickUserCan( 'patrol' ) ) {
480484 $sk = $wgUser->getSkin();
481485 $token = $wgUser->editToken( $this->mRcidMarkPatrolled );
 486+ $wgOut->preventClickjacking();
482487 $wgOut->addHTML(
483488 "<div class='patrollink'>[" . $sk->link(
484489 $this->mTitle,
Index: branches/REL1_17/phase3/includes/Article.php
@@ -893,6 +893,9 @@
894894 return;
895895 }
896896
 897+ # Allow frames by default
 898+ $wgOut->allowClickjacking();
 899+
897900 if ( !$wgUseETag && !$this->mTitle->quickUserCan( 'edit' ) ) {
898901 $parserOptions->setEditSection( false );
899902 }
@@ -1311,6 +1314,7 @@
13121315
13131316 $sk = $wgUser->getSkin();
13141317 $token = $wgUser->editToken( $rcid );
 1318+ $wgOut->preventClickjacking();
13151319
13161320 $wgOut->addHTML(
13171321 "<div class='patrollink'>" .
@@ -1591,6 +1595,8 @@
15921596 return;
15931597 }
15941598
 1599+ $wgOut->preventClickjacking();
 1600+
15951601 $tbtext = "";
15961602 foreach ( $tbs as $o ) {
15971603 $rmvtxt = "";
Property changes on: branches/REL1_17/phase3/includes/Article.php
___________________________________________________________________
Modified: svn:mergeinfo
15981604 Merged /trunk/phase3/includes/Article.php:r79561
Index: branches/REL1_17/phase3/includes/ImagePage.php
@@ -601,6 +601,7 @@
602602 $this->loadFile();
603603 $pager = new ImageHistoryPseudoPager( $this );
604604 $wgOut->addHTML( $pager->getBody() );
 605+ $wgOut->preventClickjacking( $pager->getPreventClickjacking() );
605606
606607 $this->img->resetHistory(); // free db resources
607608
@@ -828,6 +829,7 @@
829830 class ImageHistoryList {
830831
831832 protected $imagePage, $img, $skin, $title, $repo, $showThumb;
 833+ protected $preventClickjacking = false;
832834
833835 public function __construct( $imagePage ) {
834836 global $wgUser, $wgShowArchiveThumbnails;
@@ -954,6 +956,7 @@
955957 # Don't link to unviewable files
956958 $row .= '<span class="history-deleted">' . $wgLang->timeAndDate( $timestamp, true ) . '</span>';
957959 } elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
 960+ $this->preventClickjacking();
958961 $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
959962 # Make a link to review the image
960963 $url = $this->skin->link(
@@ -1041,9 +1044,19 @@
10421045 return wfMsgHtml( 'filehist-nothumb' );
10431046 }
10441047 }
 1048+
 1049+ protected function preventClickjacking( $enable = true ) {
 1050+ $this->preventClickjacking = $enable;
 1051+ }
 1052+
 1053+ public function getPreventClickjacking() {
 1054+ return $this->preventClickjacking;
 1055+ }
10451056 }
10461057
10471058 class ImageHistoryPseudoPager extends ReverseChronologicalPager {
 1059+ protected $preventClickjacking = false;
 1060+
10481061 function __construct( $imagePage ) {
10491062 parent::__construct();
10501063 $this->mImagePage = $imagePage;
@@ -1084,6 +1097,10 @@
10851098 $s .= $list->imageHistoryLine( !$file->isOld(), $file );
10861099 }
10871100 $s .= $list->endImageHistoryList( $navLink );
 1101+
 1102+ if ( $list->getPreventClickjacking() ) {
 1103+ $this->preventClickjacking();
 1104+ }
10881105 }
10891106 return $s;
10901107 }
@@ -1166,4 +1183,13 @@
11671184 }
11681185 $this->mQueryDone = true;
11691186 }
 1187+
 1188+ protected function preventClickjacking( $enable = true ) {
 1189+ $this->preventClickjacking = $enable;
 1190+ }
 1191+
 1192+ public function getPreventClickjacking() {
 1193+ return $this->preventClickjacking;
 1194+ }
 1195+
11701196 }
Index: branches/REL1_17/phase3/includes/HTMLForm.php
@@ -327,6 +327,9 @@
328328 function displayForm( $submitResult ) {
329329 global $wgOut;
330330
 331+ # For good measure (it is the default)
 332+ $wgOut->preventClickjacking();
 333+
331334 $html = ''
332335 . $this->getErrors( $submitResult )
333336 . $this->mHeader
Index: branches/REL1_17/phase3/includes/OutputPage.php
@@ -48,6 +48,7 @@
4949 var $mPageTitleActionText = '';
5050 var $mParseWarnings = array();
5151 var $mSquidMaxage = 0;
 52+ var $mPreventClickjacking = true;
5253 var $mRevisionId = null;
5354 protected $mTitle = null;
5455
@@ -1443,6 +1444,41 @@
14441445 }
14451446
14461447 /**
 1448+ * Set a flag which will cause an X-Frame-Options header appropriate for
 1449+ * edit pages to be sent. The header value is controlled by
 1450+ * $wgEditPageFrameOptions.
 1451+ *
 1452+ * This is the default for special pages. If you display a CSRF-protected
 1453+ * form on an ordinary view page, then you need to call this function.
 1454+ */
 1455+ public function preventClickjacking( $enable = true ) {
 1456+ $this->mPreventClickjacking = $enable;
 1457+ }
 1458+
 1459+ /**
 1460+ * Turn off frame-breaking. Alias for $this->preventClickjacking(false).
 1461+ * This can be called from pages which do not contain any CSRF-protected
 1462+ * HTML form.
 1463+ */
 1464+ public function allowClickjacking() {
 1465+ $this->mPreventClickjacking = false;
 1466+ }
 1467+
 1468+ /**
 1469+ * Get the X-Frame-Options header value (without the name part), or false
 1470+ * if there isn't one. This is used by Skin to determine whether to enable
 1471+ * JavaScript frame-breaking, for clients that don't support X-Frame-Options.
 1472+ */
 1473+ public function getFrameOptions() {
 1474+ global $wgBreakFrames, $wgEditPageFrameOptions;
 1475+ if ( $wgBreakFrames ) {
 1476+ return 'DENY';
 1477+ } elseif ( $this->mPreventClickjacking && $wgEditPageFrameOptions ) {
 1478+ return $wgEditPageFrameOptions;
 1479+ }
 1480+ }
 1481+
 1482+ /**
14471483 * Send cache control HTTP headers
14481484 */
14491485 public function sendCacheControl() {
@@ -1666,6 +1702,12 @@
16671703 $wgRequest->response()->header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" );
16681704 $wgRequest->response()->header( 'Content-language: ' . $wgLanguageCode );
16691705
 1706+ // Prevent framing, if requested
 1707+ $frameOptions = $this->getFrameOptions();
 1708+ if ( $frameOptions ) {
 1709+ $wgRequest->response()->header( "X-Frame-Options: $frameOptions" );
 1710+ }
 1711+
16701712 if ( $this->mArticleBodyOnly ) {
16711713 $this->out( $this->mBodytext );
16721714 } else {
Property changes on: branches/REL1_17/phase3/includes/OutputPage.php
___________________________________________________________________
Modified: svn:mergeinfo
16731715 Merged /trunk/phase3/includes/OutputPage.php:r79561
Index: branches/REL1_17/phase3/includes/HistoryPage.php
@@ -171,6 +171,7 @@
172172 $pager->getBody() .
173173 $pager->getNavigationBar()
174174 );
 175+ $wgOut->preventClickjacking( $pager->getPreventClickjacking() );
175176
176177 wfProfileOut( __METHOD__ );
177178 }
@@ -309,6 +310,7 @@
310311 class HistoryPager extends ReverseChronologicalPager {
311312 public $lastRow = false, $counter, $historyPage, $title, $buttons, $conds;
312313 protected $oldIdChecked;
 314+ protected $preventClickjacking = false;
313315
314316 function __construct( $historyPage, $year = '', $month = '', $tagFilter = '', $conds = array() ) {
315317 parent::__construct();
@@ -399,6 +401,7 @@
400402 ) . "\n";
401403
402404 if ( $wgUser->isAllowed( 'deleterevision' ) ) {
 405+ $this->preventClickjacking();
403406 $float = $wgContLang->alignEnd();
404407 # Note bug #20966, <button> is non-standard in IE<8
405408 $element = Html::element( 'button',
@@ -415,6 +418,7 @@
416419 $this->buttons .= $element;
417420 }
418421 if ( $wgUser->isAllowed( 'revisionmove' ) ) {
 422+ $this->preventClickjacking();
419423 $float = $wgContLang->alignEnd();
420424 # Note bug #20966, <button> is non-standard in IE<8
421425 $element = Html::element( 'button',
@@ -516,6 +520,7 @@
517521 $del = '';
518522 // Show checkboxes for each revision
519523 if ( $wgUser->isAllowed( 'deleterevision' ) || $wgUser->isAllowed( 'revisionmove' ) ) {
 524+ $this->preventClickjacking();
520525 // If revision was hidden from sysops, disable the checkbox
521526 // However, if the user has revisionmove rights, we cannot disable the checkbox
522527 if ( !$rev->userCan( Revision::DELETED_RESTRICTED ) && !$wgUser->isAllowed( 'revisionmove' ) ) {
@@ -565,6 +570,7 @@
566571 # Rollback and undo links
567572 if ( !is_null( $next ) && is_object( $next ) ) {
568573 if ( $latest && $this->title->userCan( 'rollback' ) && $this->title->userCan( 'edit' ) ) {
 574+ $this->preventClickjacking();
569575 $tools[] = '<span class="mw-rollback-link">' .
570576 $this->getSkin()->buildRollbackLink( $rev ) . '</span>';
571577 }
@@ -754,6 +760,20 @@
755761 return '';
756762 }
757763 }
 764+
 765+ /**
 766+ * This is called if a write operation is possible from the generated HTML
 767+ */
 768+ function preventClickjacking( $enable = true ) {
 769+ $this->preventClickjacking = $enable;
 770+ }
 771+
 772+ /**
 773+ * Get the "prevent clickjacking" flag
 774+ */
 775+ function getPreventClickjacking() {
 776+ return $this->preventClickjacking;
 777+ }
758778 }
759779
760780 /**
Property changes on: branches/REL1_17/phase3/includes/HistoryPage.php
___________________________________________________________________
Modified: svn:mergeinfo
761781 Merged /trunk/phase3/includes/HistoryPage.php:r79561
Index: branches/REL1_17/phase3/includes/installer/WebInstallerOutput.php
@@ -162,7 +162,8 @@
163163 $this->headerDone = true;
164164 $dbTypes = $this->parent->getDBTypes();
165165
166 - $this->parent->request->response()->header("Content-Type: text/html; charset=utf-8");
 166+ $this->parent->request->response()->header( 'Content-Type: text/html; charset=utf-8' );
 167+ $this->parent->request->response()->header( 'X-Frame-Options: DENY' );
167168 if ( $this->redirectTarget ) {
168169 $this->parent->request->response()->header( 'Location: '.$this->redirectTarget );
169170 return;
Index: branches/REL1_17/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php
@@ -30,7 +30,7 @@
3131
3232 protected function getConfig( $context ) {
3333 global $wgLoadScript, $wgScript, $wgStylePath, $wgScriptExtension,
34 - $wgArticlePath, $wgScriptPath, $wgServer, $wgContLang, $wgBreakFrames,
 34+ $wgArticlePath, $wgScriptPath, $wgServer, $wgContLang,
3535 $wgVariantArticlePath, $wgActionPaths, $wgUseAjax, $wgVersion,
3636 $wgEnableAPI, $wgEnableWriteAPI, $wgDBname, $wgEnableMWSuggest,
3737 $wgSitename, $wgFileExtensions;
@@ -66,7 +66,6 @@
6767 'wgServer' => $wgServer,
6868 'wgUserLanguage' => $context->getLanguage(),
6969 'wgContentLanguage' => $wgContLang->getCode(),
70 - 'wgBreakFrames' => $wgBreakFrames,
7170 'wgVersion' => $wgVersion,
7271 'wgEnableAPI' => $wgEnableAPI,
7372 'wgEnableWriteAPI' => $wgEnableWriteAPI,
Index: branches/REL1_17/phase3/includes/DefaultSettings.php
@@ -2260,12 +2260,33 @@
22612261 $wgEnableTooltipsAndAccesskeys = true;
22622262
22632263 /**
2264 - * Break out of framesets. This can be used to prevent external sites from
2265 - * framing your site with ads.
 2264+ * Break out of framesets. This can be used to prevent clickjacking attacks,
 2265+ * or to prevent external sites from framing your site with ads.
22662266 */
22672267 $wgBreakFrames = false;
22682268
22692269 /**
 2270+ * The X-Frame-Options header to send on pages sensitive to clickjacking
 2271+ * attacks, such as edit pages. This prevents those pages from being displayed
 2272+ * in a frame or iframe. The options are:
 2273+ *
 2274+ * - 'DENY': Do not allow framing. This is recommended for most wikis.
 2275+ *
 2276+ * - 'SAMEORIGIN': Allow framing by pages on the same domain. This can be used
 2277+ * to allow framing within a trusted domain. This is insecure if there
 2278+ * is a page on the same domain which allows framing of arbitrary URLs.
 2279+ *
 2280+ * - false: Allow all framing. This opens up the wiki to XSS attacks and thus
 2281+ * full compromise of local user accounts. Private wikis behind a
 2282+ * corporate firewall are especially vulnerable. This is not
 2283+ * recommended.
 2284+ *
 2285+ * For extra safety, set $wgBreakFrames = true, to prevent framing on all pages,
 2286+ * not just edit pages.
 2287+ */
 2288+$wgEditPageFrameOptions = 'DENY';
 2289+
 2290+/**
22702291 * Disable output compression (enabled by default if zlib is available)
22712292 */
22722293 $wgDisableOutputCompression = false;
Index: branches/REL1_17/phase3/includes/specials/SpecialAllpages.php
@@ -62,6 +62,7 @@
6363
6464 $this->setHeaders();
6565 $this->outputHeader();
 66+ $wgOut->allowClickjacking();
6667
6768 # GET values
6869 $from = $wgRequest->getVal( 'from', null );
Index: branches/REL1_17/phase3/includes/specials/SpecialCategories.php
@@ -35,6 +35,7 @@
3636
3737 $this->setHeaders();
3838 $this->outputHeader();
 39+ $wgOut->allowClickjacking();
3940
4041 $from = $wgRequest->getText( 'from', $par );
4142
Index: branches/REL1_17/phase3/includes/specials/SpecialSpecialpages.php
@@ -33,8 +33,10 @@
3434 }
3535
3636 function execute( $par ) {
 37+ global $wgOut;
3738 $this->setHeaders();
3839 $this->outputHeader();
 40+ $wgOut->allowClickjacking();
3941
4042 $groups = $this->getPageGroups();
4143
Index: branches/REL1_17/phase3/includes/specials/SpecialContributions.php
@@ -139,6 +139,7 @@
140140 '<p>' . $pager->getNavigationBar() . '</p>'
141141 );
142142 }
 143+ $wgOut->preventClickjacking( $pager->getPreventClickjacking() );
143144
144145
145146 # Show the appropriate "footer" message - WHOIS tools, etc.
@@ -481,6 +482,7 @@
482483 public $mDefaultDirection = true;
483484 var $messages, $target;
484485 var $namespace = '', $mDb;
 486+ var $preventClickjacking = false;
485487
486488 function __construct( $options ) {
487489 parent::__construct();
@@ -629,6 +631,7 @@
630632 if( !$row->page_is_new && $page->quickUserCan( 'rollback' )
631633 && $page->quickUserCan( 'edit' ) )
632634 {
 635+ $this->preventClickjacking();
633636 $topmarktext .= ' '.$sk->generateRollback( $rev );
634637 }
635638 }
@@ -749,4 +752,11 @@
750753 }
751754 }
752755
 756+ protected function preventClickjacking() {
 757+ $this->preventClickjacking = true;
 758+ }
 759+
 760+ public function getPreventClickjacking() {
 761+ return $this->preventClickjacking;
 762+ }
753763 }
Index: branches/REL1_17/phase3/includes/specials/SpecialVersion.php
@@ -53,6 +53,7 @@
5454
5555 $this->setHeaders();
5656 $this->outputHeader();
 57+ $wgOut->allowClickjacking();
5758
5859 $wgOut->addHTML( Xml::openElement( 'div',
5960 array( 'dir' => $wgContLang->getDir() ) ) );
Property changes on: branches/REL1_17/phase3/includes/specials/SpecialVersion.php
___________________________________________________________________
Modified: svn:mergeinfo
6061 Merged /trunk/phase3/includes/specials/SpecialVersion.php:r79561
Index: branches/REL1_17/phase3/includes/specials/SpecialSearch.php
@@ -29,7 +29,9 @@
3030 * @param $par String: (default '')
3131 */
3232 function wfSpecialSearch( $par = '' ) {
33 - global $wgRequest, $wgUser;
 33+ global $wgRequest, $wgUser, $wgOut;
 34+ $wgOut->allowClickjacking();
 35+
3436 // Strip underscores from title parameter; most of the time we'll want
3537 // text form here. But don't strip underscores from actual text params!
3638 $titleParam = str_replace( '_', ' ', $par );
Property changes on: branches/REL1_17/phase3/includes/specials/SpecialSearch.php
___________________________________________________________________
Modified: svn:mergeinfo
3739 Merged /trunk/phase3/includes/specials/SpecialSearch.php:r79561
Index: branches/REL1_17/phase3/includes/specials/SpecialLinkSearch.php
@@ -62,6 +62,7 @@
6363
6464 $self = Title::makeTitle( NS_SPECIAL, 'Linksearch' );
6565
 66+ $wgOut->allowClickjacking();
6667 $wgOut->addWikiMsg( 'linksearch-text', '<nowiki>' . $wgLang->commaList( $wgUrlProtocols ) . '</nowiki>' );
6768 $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) .
6869 Html::hidden( 'title', $self->getPrefixedDbKey() ) .
Index: branches/REL1_17/phase3/includes/Skin.php
@@ -440,6 +440,7 @@
441441 'wgUserGroups' => $wgUser->getEffectiveGroups(),
442442 'wgCurRevisionId' => isset( $wgArticle ) ? $wgArticle->getLatest() : 0,
443443 'wgCategories' => $wgOut->getCategories(),
 444+ 'wgBreakFrames' => $wgOut->getFrameOptions() == 'DENY',
444445 );
445446 foreach ( $wgTitle->getRestrictionTypes() as $type ) {
446447 $vars['wgRestriction' . ucfirst( $type )] = $wgTitle->getRestrictions( $type );

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r79561Fix for bug 26561: clickjacking attacks. See the bug report for full document...tstarling06:12, 4 January 2011

Status & tagging log