From: Brion Vibber Date: Mon, 21 Feb 2005 01:56:50 +0000 (+0000) Subject: Audit tweaks: extra post checks, markup fixes. X-Git-Tag: 1.5.0alpha1~711 X-Git-Url: http://git.cyclocoop.org/%24image?a=commitdiff_plain;h=5a9e8c8c625ce677fc5b0c646ad02784e5b78723;p=lhc%2Fweb%2Fwiklou.git Audit tweaks: extra post checks, markup fixes. --- diff --git a/includes/Article.php b/includes/Article.php index cd97451136..d869c8b066 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -1214,7 +1214,9 @@ class Article { return; } - $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) && $wgRequest->wasPosted(); + $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) && + $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ); $moveonly = $wgRequest->getBool( 'wpMoveOnly' ); $reason = $wgRequest->getText( 'wpReasonProtect' ); @@ -1266,7 +1268,7 @@ class Article { * Output protection confirmation dialog */ function confirmProtect( $par, $reason, $limit = 'sysop' ) { - global $wgOut; + global $wgOut, $wgUser; wfDebug( "Article::confirmProtect\n" ); @@ -1295,6 +1297,7 @@ class Article { } $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( "
@@ -1337,6 +1340,7 @@ class Article { +
\n" ); $wgOut->returnToMain( false ); @@ -1355,7 +1359,9 @@ class Article { function delete() { global $wgUser, $wgOut, $wgMessageCache, $wgRequest; $fname = 'Article::delete'; - $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted(); + $confirm = $wgRequest->getBool( 'wpConfirm' ) && + $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ); $reason = $wgRequest->getText( 'wpReason' ); # This code desperately needs to be totally rewritten @@ -1464,7 +1470,7 @@ class Article { * Output deletion confirmation dialog */ function confirmDelete( $par, $reason ) { - global $wgOut; + global $wgOut, $wgUser; wfDebug( "Article::confirmDelete\n" ); @@ -1478,6 +1484,7 @@ class Article { $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); $check = htmlspecialchars( wfMsg( 'confirmcheck' ) ); $delcom = htmlspecialchars( wfMsg( 'deletecomment' ) ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( "
@@ -1508,6 +1515,7 @@ class Article { +
\n" ); $wgOut->returnToMain( false ); @@ -1663,6 +1671,13 @@ class Article { $wgOut->readOnlyPage( $this->getContent( true ) ); return; } + if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ), + array( $this->mTitle->getPrefixedText(), + $wgRequest->getVal( 'from' ) ) ) ) { + $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); + $wgOut->addWikiText( wfMsg( 'sessionfailure' ) ); + return; + } $dbw =& wfGetDB( DB_MASTER ); # Enhanced rollback, marks edits rc_bot=1 diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php index 7d910aa842..603fadf1a2 100644 --- a/includes/DifferenceEngine.php +++ b/includes/DifferenceEngine.php @@ -117,7 +117,9 @@ class DifferenceEngine { 'target=' . urlencode($this->mNewUser) ); if ( !$this->mNewid && $wgUser->isAllowed('rollback') ) { $rollback = '   [' . $sk->makeKnownLinkObj( $wgTitle, wfMsg( 'rollbacklink' ), - 'action=rollback&from=' . urlencode($this->mNewUser) ) . ']'; + 'action=rollback&from=' . urlencode($this->mNewUser) . + '&token=' . urlencode( $wgUser->editToken( array( $wgTitle->getPrefixedText(), $this->mNewUser ) ) ) ) . + ']'; } else { $rollback = ''; } diff --git a/includes/ImagePage.php b/includes/ImagePage.php index d5cf1c0427..be571836ed 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -212,7 +212,11 @@ class ImagePage extends Article { # Deleting old images doesn't require confirmation if ( !is_null( $oldimage ) || $confirm ) { - $this->doDelete(); + if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) { + $this->doDelete(); + } else { + $wgOut->fatalError( wfMsg( 'sessionfailure' ) ); + } return; } @@ -233,12 +237,19 @@ class ImagePage extends Article { $fname = 'ImagePage::doDelete'; $reason = $wgRequest->getVal( 'wpReason' ); - $image = $wgRequest->getVal( 'image' ); $oldimage = $wgRequest->getVal( 'oldimage' ); $dbw =& wfGetDB( DB_MASTER ); if ( !is_null( $oldimage ) ) { + if ( strlen( $oldimage ) < 16 ) { + $wgOut->unexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); + return; + } + if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) { + $wgOut->unexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); + return; + } # Squid purging if ( $wgUseSquid ) { $urlArr = Array( @@ -250,9 +261,7 @@ class ImagePage extends Article { $dbw->delete( 'oldimage', array( 'oi_archive_name' => $oldimage ) ); $deleted = $oldimage; } else { - if ( is_null ( $image ) ) { - $image = $this->mTitle->getDBkey(); - } + $image = $this->mTitle->getDBkey(); $dest = wfImageDir( $image ); $archive = wfImageDir( $image ); @@ -342,7 +351,7 @@ class ImagePage extends Article { function revert() { - global $wgOut, $wgRequest; + global $wgOut, $wgRequest, $wgUser; global $wgUseSquid, $wgInternalServer, $wgDeferredUpdateList; $oldimage = $wgRequest->getText( 'oldimage' ); @@ -367,6 +376,10 @@ class ImagePage extends Article { $wgOut->sysopRequired(); return; } + if( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) { + $wgOut->errorpage( 'internalerror', 'sessionfailure' ); + return; + } $name = substr( $oldimage, 15 ); $dest = wfImageDir( $name ); @@ -450,11 +463,13 @@ class ImageHistoryList { } else { $url = htmlspecialchars( wfImageArchiveUrl( $img ) ); if( $wgUser->getID() != 0 && $wgTitle->userCanEdit() ) { + $token = urlencode( $wgUser->editToken( $img ) ); $rlink = $this->skin->makeKnownLink( $wgTitle->getPrefixedText(), wfMsg( 'revertimg' ), 'action=revert&oldimage=' . - urlencode( $img ) ); + urlencode( $img ) . "&wpEditToken=$token" ); $dlink = $this->skin->makeKnownLink( $wgTitle->getPrefixedText(), - $del, 'action=delete&oldimage=' . urlencode( $img ) ); + $del, 'action=delete&oldimage=' . urlencode( $img ) . + "&wpEditToken=$token" ); } else { # Having live active links for non-logged in users # means that bots and spiders crawling our site can diff --git a/includes/Linker.php b/includes/Linker.php index ee36c912da..b02c88b4e6 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -551,9 +551,7 @@ class Linker { $url = $img->getViewURL(); #$label = htmlspecialchars( $label ); - $alt = preg_replace( '/<[^>]*>/', '', $label); - $alt = preg_replace('/&(?!:amp;|#[Xx][0-9A-fa-f]+;|#[0-9]+;|[a-zA-Z0-9]+;)/', '&', $alt); - $alt = str_replace( array('<', '>', '"'), array('<', '>', '"'), $alt ); + $alt = Sanitizer::stripAllTags( $label ); $width = $height = 0; if ( $img->exists() ) @@ -637,28 +635,36 @@ class Linker { return $this->makeMediaLinkObj( $nt, $alt ); } - /** @todo document */ - function makeMediaLinkObj( $nt, $alt = '', $nourl=false ) { - if ( ! isset( $nt ) ) - { + /** + * Create a direct link to a given uploaded file. + * + * @param Title $title + * @param string $text pre-sanitized HTML + * @param bool $nourl Mask absolute URLs, so the parser doesn't + * linkify them (it is currently not context-aware) + * @return string HTML + * + * @access public + * @todo Handle invalid or missing images better. + */ + function makeMediaLinkObj( $title, $text = '', $nourl=false ) { + if( is_null( $title ) ) { ### HOTFIX. Instead of breaking, return empty string. - $s = $alt; + return $text; } else { - $name = $nt->getDBKey(); - $img = Image::newFromTitle( $nt ); - $url = $img->getURL(); - # $nourl can be set by the parser - # this is a hack to mask absolute URLs, so the parser doesn't - # linkify them (it is currently not context-aware) - # 2004-10-25 - if ($nourl) { $url=str_replace("http://","http-noparse://",$url) ; } - if ( empty( $alt ) ) { - $alt = preg_replace( '/\.(.+?)^/', '', $name ); + $name = $title->getDBKey(); + $img = Image::newFromTitle( $title ); + $url = $img->getURL(); + if( $nourl ) { + $url = str_replace( "http://", "http-noparse://", $url ); + } + $alt = htmlspecialchars( $title->getText() ); + if( $text == '' ) { + $text = $alt; } $u = htmlspecialchars( $url ); - $s = "{$alt}"; + return "{$text}"; } - return $s; } /** @todo document */ @@ -778,7 +784,10 @@ class Linker { * parameter level defines if we are on an indentation level */ function tocLine( $anchor, $tocline, $tocnumber, $level ) { - return "\n
  • ' . $tocnumber . ' ' . $tocline . ''; + return "\n
  • ' . + $tocnumber . ' ' . + $tocline . ''; } /** @todo document */ diff --git a/includes/SpecialBlockip.php b/includes/SpecialBlockip.php index bed961b9fb..1671b43dbb 100644 --- a/includes/SpecialBlockip.php +++ b/includes/SpecialBlockip.php @@ -19,9 +19,14 @@ function wfSpecialBlockip() { $ipb = new IPBlockForm(); $action = $wgRequest->getVal( 'action' ); - if ( 'success' == $action ) { $ipb->showSuccess(); } - else if ( $wgRequest->wasPosted() && 'submit' == $action ) { $ipb->doSubmit(); } - else { $ipb->showForm( '' ); } + if ( 'success' == $action ) { + $ipb->showSuccess(); + } else if ( $wgRequest->wasPosted() && 'submit' == $action && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { + $ipb->doSubmit(); + } else { + $ipb->showForm( '' ); + } } /** @@ -66,6 +71,7 @@ class IPBlockForm { $scBlockAddress = htmlspecialchars( $this->BlockAddress ); $scBlockExpiry = htmlspecialchars( $this->BlockExpiry ); $scBlockReason = htmlspecialchars( $this->BlockReason ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( "
    @@ -106,6 +112,7 @@ class IPBlockForm { +
    \n" ); } diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php index 6834568136..efb4f0ca96 100644 --- a/includes/SpecialContributions.php +++ b/includes/SpecialContributions.php @@ -187,6 +187,8 @@ function ucListEdit( $sk, $ns, $t, $ts, $topmark, $comment, $isminor, $isnew, $t if( $wgUser->isAllowed('rollback') ) { $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : ''; + $extraRollback .= '&token=' . urlencode( + $wgUser->editToken( array( $page->getPrefixedText(), $target ) ) ); # $target = $wgRequest->getText( 'target' ); $topmarktext .= ' ['. $sk->makeKnownLinkObj( $page, $messages['rollbacklink'], diff --git a/includes/SpecialEmailuser.php b/includes/SpecialEmailuser.php index 92b938e350..8845e93735 100644 --- a/includes/SpecialEmailuser.php +++ b/includes/SpecialEmailuser.php @@ -57,9 +57,14 @@ function wfSpecialEmailuser( $par ) { $f = new EmailUserForm( $nu->getName() . " <{$address}>", $target ); - if ( "success" == $action ) { $f->showSuccess(); } - else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); } - else { $f->showForm(); } + if ( "success" == $action ) { + $f->showSuccess(); + } else if ( "submit" == $action && $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { + $f->doSubmit(); + } else { + $f->showForm(); + } } /** @@ -103,6 +108,7 @@ class EmailUserForm { $titleObj = Title::makeTitle( NS_SPECIAL, "Emailuser" ); $action = $titleObj->escapeLocalURL( "target=" . urlencode( $this->target ) . "&action=submit" ); + $token = $wgUser->editToken(); $wgOut->addHTML( "
    @@ -126,6 +132,7 @@ class EmailUserForm {   +
    \n" ); } diff --git a/includes/SpecialIpblocklist.php b/includes/SpecialIpblocklist.php index 8f1129bbd2..c186786339 100644 --- a/includes/SpecialIpblocklist.php +++ b/includes/SpecialIpblocklist.php @@ -20,7 +20,8 @@ function wfSpecialIpblocklist() { if ( "success" == $action ) { $msg = wfMsg( "ipusuccess", htmlspecialchars( $ip ) ); $ipu->showList( $msg ); - } else if ( "submit" == $action && $wgRequest->wasPosted() ) { + } else if ( "submit" == $action && $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { if ( ! $wgUser->isAllowed('block') ) { $wgOut->sysopRequired(); return; @@ -63,6 +64,7 @@ class IPUnblockForm { $wgOut->setSubtitle( wfMsg( "formerror" ) ); $wgOut->addHTML( "

    {$err}

    \n" ); } + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( "
    @@ -86,6 +88,7 @@ class IPUnblockForm { +
    \n" ); } diff --git a/includes/SpecialLockdb.php b/includes/SpecialLockdb.php index db07f45822..bc07b041a4 100644 --- a/includes/SpecialLockdb.php +++ b/includes/SpecialLockdb.php @@ -19,9 +19,14 @@ function wfSpecialLockdb() $action = $wgRequest->getVal( 'action' ); $f = new DBLockForm(); - if ( "success" == $action ) { $f->showSuccess(); } - else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); } - else { $f->showForm( "" ); } + if ( "success" == $action ) { + $f->showSuccess(); + } else if ( "submit" == $action && $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { + $f->doSubmit(); + } else { + $f->showForm( "" ); + } } /** @@ -53,6 +58,7 @@ class DBLockForm { $elr = htmlspecialchars( wfMsg( "enterlockreason" ) ); $titleObj = Title::makeTitle( NS_SPECIAL, "Lockdb" ); $action = $titleObj->escapeLocalURL( "action=submit" ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( << @@ -72,6 +78,7 @@ class DBLockForm { + END ); diff --git a/includes/SpecialMovepage.php b/includes/SpecialMovepage.php index 89922b402f..140b254c3d 100644 --- a/includes/SpecialMovepage.php +++ b/includes/SpecialMovepage.php @@ -29,9 +29,14 @@ function wfSpecialMovepage() { $f = new MovePageForm(); - if ( 'success' == $action ) { $f->showSuccess(); } - else if ( 'submit' == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); } - else { $f->showForm( '' ); } + if ( 'success' == $action ) { + $f->showSuccess(); + } else if ( 'submit' == $action && $wgRequest->wasPosted() + && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { + $f->doSubmit(); + } else { + $f->showForm( '' ); + } } /** @@ -82,6 +87,7 @@ class MovePageForm { $titleObj = Title::makeTitle( NS_SPECIAL, 'Movepage' ); $action = $titleObj->escapeLocalURL( 'action=submit' ); + $token = htmlspecialchars( $wgUser->editToken() ); if ( $err != '' ) { $wgOut->setSubtitle( wfMsg( 'formerror' ) ); @@ -119,6 +125,7 @@ class MovePageForm { + \n" ); } diff --git a/includes/SpecialPreferences.php b/includes/SpecialPreferences.php index 58036a9bfd..e2c16f6c80 100644 --- a/includes/SpecialPreferences.php +++ b/includes/SpecialPreferences.php @@ -39,7 +39,7 @@ class PreferencesForm { * Load some values */ function PreferencesForm( &$request ) { - global $wgLang, $wgContLang, $wgAllowRealName; + global $wgLang, $wgContLang, $wgUser, $wgAllowRealName; $this->mQuickbar = $request->getVal( 'wpQuickbar' ); $this->mOldpass = $request->getVal( 'wpOldpass' ); @@ -67,7 +67,9 @@ class PreferencesForm { $this->mAction = $request->getVal( 'action' ); $this->mReset = $request->getCheck( 'wpReset' ); $this->mPosted = $request->wasPosted(); - $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) && $this->mPosted; + $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) && + $this->mPosted && + $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) ); # User toggles (the big ugly unsorted list of checkboxes) $this->mToggles = array(); @@ -686,6 +688,7 @@ class PreferencesForm { } $wgOut->addHTML( "\n\n" ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( "
    @@ -696,6 +699,7 @@ class PreferencesForm {
    + \n" ); } } diff --git a/includes/SpecialUndelete.php b/includes/SpecialUndelete.php index dbfe7cced7..9d874d4f8f 100644 --- a/includes/SpecialUndelete.php +++ b/includes/SpecialUndelete.php @@ -275,10 +275,13 @@ class UndeleteForm { var $mTargetTimestamp; function UndeleteForm( &$request, $par = "" ) { + global $wgUser; $this->mAction = $request->getText( 'action' ); $this->mTarget = $request->getText( 'target' ); $this->mTimestamp = $request->getText( 'timestamp' ); - $this->mRestore = $request->getCheck( 'restore' ) && $request->wasPosted(); + $this->mRestore = $request->getCheck( 'restore' ) && + $request->wasPosted() && + $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) ); if( $par != "" ) { $this->mTarget = $par; } @@ -376,11 +379,13 @@ class UndeleteForm { $action = $titleObj->escapeLocalURL( "action=submit" ); $encTarget = htmlspecialchars( $this->mTarget ); $button = htmlspecialchars( wfMsg("undeletebtn") ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML("
    + "); # Show relevant lines from the deletion log: diff --git a/includes/SpecialUnlockdb.php b/includes/SpecialUnlockdb.php index 222a32417a..9184ab0360 100644 --- a/includes/SpecialUnlockdb.php +++ b/includes/SpecialUnlockdb.php @@ -18,9 +18,14 @@ function wfSpecialUnlockdb() { $action = $wgRequest->getVal( 'action' ); $f = new DBUnlockForm(); - if ( "success" == $action ) { $f->showSuccess(); } - else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); } - else { $f->showForm( "" ); } + if ( "success" == $action ) { + $f->showSuccess(); + } else if ( "submit" == $action && $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { + $f->doSubmit(); + } else { + $f->showForm( "" ); + } } /** @@ -44,6 +49,7 @@ class DBUnlockForm { $lb = htmlspecialchars( wfMsg( "unlockbtn" ) ); $titleObj = Title::makeTitle( NS_SPECIAL, "Unlockdb" ); $action = $titleObj->escapeLocalURL( "action=submit" ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( << + END ); diff --git a/includes/SpecialUpload.php b/includes/SpecialUpload.php index 2a9bd399e3..75b12a367f 100644 --- a/includes/SpecialUpload.php +++ b/includes/SpecialUpload.php @@ -595,7 +595,8 @@ class UploadForm { * @return bool */ function verify( $tmpfile, $extension ) { - if( $this->triggersIEbug( $tmpfile ) ) { + if( $this->triggersIEbug( $tmpfile ) || + $this->triggersSafariBug( $tmpfile ) ) { return false; } @@ -681,10 +682,18 @@ class UploadForm { */ function triggersIEbug( $filename ) { $file = fopen( $filename, 'rb' ); - $chunk = strtolower( fread( $file, 200 ) ); + $chunk = strtolower( fread( $file, 256 ) ); fclose( $file ); - $tags = array( ' diff --git a/includes/User.php b/includes/User.php index 27bb4491d4..81f8130cb6 100644 --- a/includes/User.php +++ b/includes/User.php @@ -1210,15 +1210,22 @@ class User { * login credentials aren't being hijacked with a foreign form * submission. * + * @param mixed $salt - Optional function-specific data for hash. + * Use a string or an array of strings. * @return string * @access public */ - function editToken() { + function editToken( $salt = '' ) { if( !isset( $_SESSION['wsEditToken'] ) ) { $token = dechex( mt_rand() ) . dechex( mt_rand() ); $_SESSION['wsEditToken'] = $token; + } else { + $token = $_SESSION['wsEditToken']; + } + if( is_array( $salt ) ) { + $salt = implode( '|', $salt ); } - return $_SESSION['wsEditToken']; + return md5( $token . $salt ); } /** @@ -1227,14 +1234,13 @@ class User { * user's own login session, not a form submission from a third-party * site. * - * @param string $val + * @param string $val - the input value to compare + * @param string $salt - Optional function-specific data for hash * @return bool * @access public */ - function matchEditToken( $val ) { - if( !isset( $_SESSION['wsEditToken'] ) ) - return false; - return $_SESSION['wsEditToken'] == $val; + function matchEditToken( $val, $salt = '' ) { + return ( $val == $this->editToken( $salt ) ); } } diff --git a/languages/Language.php b/languages/Language.php index fb32735462..99d33c6a75 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -1300,6 +1300,9 @@ Last edit was by [[User:$3|$3]] ([[User talk:$3|Talk]]). ", # only shown if there is an edit comment 'editcomment' => "The edit comment was: \"$1\".", 'revertpage' => "Reverted edit of $2, changed back to last version by $1", +'sessionfailure' => 'There seems to be a problem with your login session; +this action has been canceled as a precaution against session hijacking. +Please hit "back" and reload the page you came from, then try again.', 'protectlogpage' => 'Protection_log', 'protectlogtext' => "Below is a list of page locks/unlocks. See [[Project:Protected page]] for more information.",