r7357 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r7356‎ | r7357 | r7358 >
Date:06:19, 4 February 2005
Author:vibber
Status:old
Tags:
Comment:
Security tweaks:
* Leave user CSS/JS off by default
* Tighten user CSS/JS preview activation
* Require logged-in edits to include an extra session credential
* ogg removed from default upload whitelist
Modified paths:
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/EditPage.php (modified) (history)
  • /trunk/phase3/includes/Skin.php (modified) (history)
  • /trunk/phase3/includes/SkinTemplate.php (modified) (history)
  • /trunk/phase3/includes/User.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/User.php
@@ -1187,6 +1187,39 @@
11881188 }
11891189 return false;
11901190 }
 1191+
 1192+ /**
 1193+ * Initialize (if necessary) and return a session token value
 1194+ * which can be used in edit forms to show that the user's
 1195+ * login credentials aren't being hijacked with a foreign form
 1196+ * submission.
 1197+ *
 1198+ * @return string
 1199+ * @access public
 1200+ */
 1201+ function editToken() {
 1202+ if( !isset( $_SESSION['wsEditToken'] ) ) {
 1203+ $token = dechex( mt_rand() ) . dechex( mt_rand() );
 1204+ $_SESSION['wsEditToken'] = $token;
 1205+ }
 1206+ return $_SESSION['wsEditToken'];
 1207+ }
 1208+
 1209+ /**
 1210+ * Check given value against the token value stored in the session.
 1211+ * A match should confirm that the form was submitted from the
 1212+ * user's own login session, not a form submission from a third-party
 1213+ * site.
 1214+ *
 1215+ * @param string $val
 1216+ * @return bool
 1217+ * @access public
 1218+ */
 1219+ function matchEditToken( $val ) {
 1220+ if( !isset( $_SESSION['wsEditToken'] ) )
 1221+ return false;
 1222+ return $_SESSION['wsEditToken'] == $val;
 1223+ }
11911224 }
11921225
11931226 ?>
Index: trunk/phase3/includes/EditPage.php
@@ -198,11 +198,17 @@
199199 # If the form is incomplete, force to preview.
200200 $this->preview = true;
201201 } else {
202 - # Some browsers will not report any submit button
203 - # if the user hits enter in the comment box.
204 - # The unmarked state will be assumed to be a save,
205 - # if the form seems otherwise complete.
206 - $this->preview = $request->getCheck( 'wpPreview' );
 202+ if( $this->tokenOk( $request ) ) {
 203+ # Some browsers will not report any submit button
 204+ # if the user hits enter in the comment box.
 205+ # The unmarked state will be assumed to be a save,
 206+ # if the form seems otherwise complete.
 207+ $this->preview = $request->getCheck( 'wpPreview' );
 208+ } else {
 209+ # Page might be a hack attempt posted from
 210+ # an external site. Preview instead of saving.
 211+ $this->preview = true;
 212+ }
207213 }
208214 $this->save = !$this->preview;
209215 if( !preg_match( '/^\d{14}$/', $this->edittime )) {
@@ -232,6 +238,24 @@
233239 $this->live = $request->getCheck( 'live' );
234240 }
235241
 242+ /**
 243+ * Make sure the form isn't faking a user's credentials.
 244+ *
 245+ * @param WebRequest $request
 246+ * @return bool
 247+ * @access private
 248+ */
 249+ function tokenOk( &$request ) {
 250+ global $wgUser;
 251+ if( $wgUser->getId() == 0 ) {
 252+ # Anonymous users may not have a session
 253+ # open. Don't tokenize.
 254+ return true;
 255+ } else {
 256+ return $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
 257+ }
 258+ }
 259+
236260 function submit() {
237261 $this->edit();
238262 }
@@ -632,6 +656,21 @@
633657 <input type='hidden' value=\"" . htmlspecialchars( $this->section ) . "\" name=\"wpSection\" />
634658 <input type='hidden' value=\"{$this->edittime}\" name=\"wpEdittime\" />\n" );
635659
 660+ if ( 0 != $wgUser->getID() ) {
 661+ /**
 662+ * To make it harder for someone to slip a user a page
 663+ * which submits an edit form to the wiki without their
 664+ * knowledge, a random token is associated with the login
 665+ * session. If it's not passed back with the submission,
 666+ * we won't save the page, or render user JavaScript and
 667+ * CSS previews.
 668+ */
 669+ $token = htmlspecialchars( $wgUser->editToken() );
 670+ $wgOut->addHTML( "
 671+<input type='hidden' value=\"$token\" name=\"wpEditToken\" />\n" );
 672+ }
 673+
 674+
636675 if ( $isConflict ) {
637676 require_once( "DifferenceEngine.php" );
638677 $wgOut->addHTML( "<h2>" . wfMsg( "yourdiff" ) . "</h2>\n" );
Index: trunk/phase3/includes/SkinTemplate.php
@@ -825,7 +825,7 @@
826826 $action = $wgRequest->getText('action');
827827
828828 # if we're previewing the CSS page, use it
829 - if($wgTitle->isCssSubpage() and $action == 'submit' and $wgTitle->userCanEditCssJsSubpage()) {
 829+ if( $wgTitle->isCssSubpage() and $this->userCanPreview( $action ) ) {
830830 $siteargs = "&smaxage=0&maxage=0";
831831 $usercss = $wgRequest->getText('wpTextbox1');
832832 } else {
@@ -864,7 +864,7 @@
865865 $action = $wgRequest->getText('action');
866866
867867 if( $wgAllowUserJs && $this->loggedin ) {
868 - if($wgTitle->isJsSubpage() and $action == 'submit' and $wgTitle->userCanEditCssJsSubpage()) {
 868+ if( $wgTitle->isJsSubpage() and $this->userCanPreview( $action ) ) {
869869 # XXX: additional security check/prompt?
870870 $this->userjsprev = '/*<![CDATA[*/ ' . $wgRequest->getText('wpTextbox1') . ' /*]]>*/';
871871 } else {
Index: trunk/phase3/includes/DefaultSettings.php
@@ -697,7 +697,7 @@
698698 * This is the list of preferred extensions for uploading files. Uploading files
699699 * with extensions not in this list will trigger a warning.
700700 */
701 -$wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg', 'ogg' );
 701+$wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' );
702702
703703 /** Files with these extensions will never be allowed as uploads. */
704704 $wgFileBlacklist = array(
@@ -912,11 +912,19 @@
913913 $wgSkinExtensionFunctions = array();
914914 $wgExtensionFunctions = array();
915915
916 -/** Allow user Javascript page? */
917 -$wgAllowUserJs = true;
 916+/**
 917+ * Allow user Javascript page?
 918+ * This enables a lot of neat customizations, but may
 919+ * increase security risk to users and server load.
 920+ */
 921+$wgAllowUserJs = false;
918922
919 -/** Allow user Cascading Style Sheets (CSS)? */
920 -$wgAllowUserCss = true;
 923+/**
 924+ * Allow user Cascading Style Sheets (CSS)?
 925+ * This enables a lot of neat customizations, but may
 926+ * increase security risk to users and server load.
 927+ */
 928+$wgAllowUserCss = false;
921929
922930 /** Use the site's Javascript page? */
923931 $wgUseSiteJs = true;
Index: trunk/phase3/includes/Skin.php
@@ -194,6 +194,30 @@
195195 return $r;
196196 }
197197
 198+ /**
 199+ * To make it harder for someone to slip a user a fake
 200+ * user-JavaScript or user-CSS preview, a random token
 201+ * is associated with the login session. If it's not
 202+ * passed back with the preview request, we won't render
 203+ * the code.
 204+ *
 205+ * @param string $action
 206+ * @return bool
 207+ * @access private
 208+ */
 209+ function userCanPreview( $action ) {
 210+ global $wgTitle, $wgRequest, $wgUser;
 211+
 212+ if( $action != 'submit' )
 213+ return false;
 214+ if( !$wgRequest->wasPosted() )
 215+ return false;
 216+ if( !$wgTitle->userCanEditCssJsSubpage() )
 217+ return false;
 218+ return $wgUser->matchEditToken(
 219+ $wgRequest->getVal( 'wpEditToken' ) );
 220+ }
 221+
198222 # get the user/site-specific stylesheet, SkinPHPTal called from RawPage.php (settings are cached that way)
199223 function getUserStylesheet() {
200224 global $wgOut, $wgStylePath, $wgContLang, $wgUser, $wgRequest, $wgTitle, $wgAllowUserCss;
@@ -202,7 +226,7 @@
203227 $s = "@import \"$wgStylePath/$sheet\";\n";
204228 if($wgContLang->isRTL()) $s .= "@import \"$wgStylePath/common/common_rtl.css\";\n";
205229 if( $wgAllowUserCss && $wgUser->getID() != 0 ) { # logged in
206 - if($wgTitle->isCssSubpage() and $action == 'submit' and $wgTitle->userCanEditCssJsSubpage()) {
 230+ if($wgTitle->isCssSubpage() && $this->userCanPreview( $action ) ) {
207231 $s .= $wgRequest->getText('wpTextbox1');
208232 } else {
209233 $userpage = $wgContLang->getNsText( Namespace::getUser() ) . ":" . $wgUser->getName();

Status & tagging log