Index: branches/REL1_4/phase3/includes/User.php |
— | — | @@ -1079,6 +1079,39 @@ |
1080 | 1080 | } |
1081 | 1081 | return false; |
1082 | 1082 | } |
| 1083 | + |
| 1084 | + /** |
| 1085 | + * Initialize (if necessary) and return a session token value |
| 1086 | + * which can be used in edit forms to show that the user's |
| 1087 | + * login credentials aren't being hijacked with a foreign form |
| 1088 | + * submission. |
| 1089 | + * |
| 1090 | + * @return string |
| 1091 | + * @access public |
| 1092 | + */ |
| 1093 | + function editToken() { |
| 1094 | + if( !isset( $_SESSION['wsEditToken'] ) ) { |
| 1095 | + $token = dechex( mt_rand() ) . dechex( mt_rand() ); |
| 1096 | + $_SESSION['wsEditToken'] = $token; |
| 1097 | + } |
| 1098 | + return $_SESSION['wsEditToken']; |
| 1099 | + } |
| 1100 | + |
| 1101 | + /** |
| 1102 | + * Check given value against the token value stored in the session. |
| 1103 | + * A match should confirm that the form was submitted from the |
| 1104 | + * user's own login session, not a form submission from a third-party |
| 1105 | + * site. |
| 1106 | + * |
| 1107 | + * @param string $val |
| 1108 | + * @return bool |
| 1109 | + * @access public |
| 1110 | + */ |
| 1111 | + function matchEditToken( $val ) { |
| 1112 | + if( !isset( $_SESSION['wsEditToken'] ) ) |
| 1113 | + return false; |
| 1114 | + return $_SESSION['wsEditToken'] == $val; |
| 1115 | + } |
1083 | 1116 | } |
1084 | 1117 | |
1085 | 1118 | ?> |
Index: branches/REL1_4/phase3/includes/EditPage.php |
— | — | @@ -94,11 +94,17 @@ |
95 | 95 | # If the form is incomplete, force to preview. |
96 | 96 | $this->preview = true; |
97 | 97 | } else { |
98 | | - # Some browsers will not report any submit button |
99 | | - # if the user hits enter in the comment box. |
100 | | - # The unmarked state will be assumed to be a save, |
101 | | - # if the form seems otherwise complete. |
102 | | - $this->preview = $request->getCheck( 'wpPreview' ); |
| 98 | + if( $this->tokenOk( $request ) ) { |
| 99 | + # Some browsers will not report any submit button |
| 100 | + # if the user hits enter in the comment box. |
| 101 | + # The unmarked state will be assumed to be a save, |
| 102 | + # if the form seems otherwise complete. |
| 103 | + $this->preview = $request->getCheck( 'wpPreview' ); |
| 104 | + } else { |
| 105 | + # Page might be a hack attempt posted from |
| 106 | + # an external site. Preview instead of saving. |
| 107 | + $this->preview = true; |
| 108 | + } |
103 | 109 | } |
104 | 110 | $this->save = !$this->preview; |
105 | 111 | if( !preg_match( '/^\d{14}$/', $this->edittime )) { |
— | — | @@ -125,6 +131,24 @@ |
126 | 132 | $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) ); |
127 | 133 | } |
128 | 134 | |
| 135 | + /** |
| 136 | + * Make sure the form isn't faking a user's credentials. |
| 137 | + * |
| 138 | + * @param WebRequest $request |
| 139 | + * @return bool |
| 140 | + * @access private |
| 141 | + */ |
| 142 | + function tokenOk( &$request ) { |
| 143 | + global $wgUser; |
| 144 | + if( $wgUser->getId() == 0 ) { |
| 145 | + # Anonymous users may not have a session |
| 146 | + # open. Don't tokenize. |
| 147 | + return true; |
| 148 | + } else { |
| 149 | + return $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) ); |
| 150 | + } |
| 151 | + } |
| 152 | + |
129 | 153 | function submit() { |
130 | 154 | $this->edit(); |
131 | 155 | } |
— | — | @@ -515,6 +539,20 @@ |
516 | 540 | <input type='hidden' value=\"" . htmlspecialchars( $this->section ) . "\" name=\"wpSection\" /> |
517 | 541 | <input type='hidden' value=\"{$this->edittime}\" name=\"wpEdittime\" />\n" ); |
518 | 542 | |
| 543 | + if ( 0 != $wgUser->getID() ) { |
| 544 | + /** |
| 545 | + * To make it harder for someone to slip a user a page |
| 546 | + * which submits an edit form to the wiki without their |
| 547 | + * knowledge, a random token is associated with the login |
| 548 | + * session. If it's not passed back with the submission, |
| 549 | + * we won't save the page, or render user JavaScript and |
| 550 | + * CSS previews. |
| 551 | + */ |
| 552 | + $token = htmlspecialchars( $wgUser->editToken() ); |
| 553 | + $wgOut->addHTML( " |
| 554 | +<input type='hidden' value=\"$token\" name=\"wpEditToken\" />\n" ); |
| 555 | + } |
| 556 | + |
519 | 557 | if ( $isConflict ) { |
520 | 558 | require_once( "DifferenceEngine.php" ); |
521 | 559 | $wgOut->addHTML( "<h2>" . wfMsg( "yourdiff" ) . "</h2>\n" ); |
Index: branches/REL1_4/phase3/includes/SkinTemplate.php |
— | — | @@ -816,7 +816,7 @@ |
817 | 817 | $action = $wgRequest->getText('action'); |
818 | 818 | |
819 | 819 | # if we're previewing the CSS page, use it |
820 | | - if($wgTitle->isCssSubpage() and $action == 'submit' and $wgTitle->userCanEditCssJsSubpage()) { |
| 820 | + if( $wgTitle->isCssSubpage() and $this->userCanPreview( $action ) ) { |
821 | 821 | # we are previewing |
822 | 822 | $siteargs = "&maxage=0"; |
823 | 823 | # use the raw css just posted directly in the header of the page |
— | — | @@ -856,7 +856,7 @@ |
857 | 857 | $action = $wgRequest->getText('action'); |
858 | 858 | |
859 | 859 | if( $wgAllowUserJs && $this->loggedin ) { |
860 | | - if($wgTitle->isJsSubpage() and $action == 'submit' and $wgTitle->userCanEditCssJsSubpage()) { |
| 860 | + if( $wgTitle->isJsSubpage() and $this->userCanPreview( $action ) ) { |
861 | 861 | # XXX: additional security check/prompt? |
862 | 862 | $this->userjsprev = '/*<![CDATA[*/ ' . $wgRequest->getText('wpTextbox1') . ' /*]]>*/'; |
863 | 863 | } else { |
Index: branches/REL1_4/phase3/includes/DefaultSettings.php |
— | — | @@ -19,7 +19,7 @@ |
20 | 20 | * MediaWiki version number |
21 | 21 | * @global string $wgVersion |
22 | 22 | */ |
23 | | -$wgVersion = '1.4beta5'; |
| 23 | +$wgVersion = '1.4beta6'; |
24 | 24 | |
25 | 25 | /** |
26 | 26 | * Name of the site. |
— | — | @@ -583,7 +583,7 @@ |
584 | 584 | |
585 | 585 | # This is the list of preferred extensions for uploading files. Uploading |
586 | 586 | # files with extensions not in this list will trigger a warning. |
587 | | -$wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg', 'ogg' ); |
| 587 | +$wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' ); |
588 | 588 | |
589 | 589 | # Files with these extensions will never be allowed as uploads. |
590 | 590 | $wgFileBlacklist = array( |
— | — | @@ -767,10 +767,14 @@ |
768 | 768 | $wgExtensionFunctions = array(); |
769 | 769 | |
770 | 770 | # Allow user Javascript page? |
771 | | -$wgAllowUserJs = true; |
| 771 | +# This enables a lot of neat customizations, but may |
| 772 | +# increase security risk to users and server load. |
| 773 | +$wgAllowUserJs = false; |
772 | 774 | |
773 | 775 | # Allow user Cascading Style Sheets (CSS)? |
774 | | -$wgAllowUserCss = true; |
| 776 | +# This enables a lot of neat customizations, but may |
| 777 | +# increase security risk to users and server load. |
| 778 | +$wgAllowUserCss = false; |
775 | 779 | |
776 | 780 | # Use the site's Javascript page? |
777 | 781 | $wgUseSiteJs = true; |
Index: branches/REL1_4/phase3/includes/Skin.php |
— | — | @@ -210,6 +210,30 @@ |
211 | 211 | return $r; |
212 | 212 | } |
213 | 213 | |
| 214 | + /** |
| 215 | + * To make it harder for someone to slip a user a fake |
| 216 | + * user-JavaScript or user-CSS preview, a random token |
| 217 | + * is associated with the login session. If it's not |
| 218 | + * passed back with the preview request, we won't render |
| 219 | + * the code. |
| 220 | + * |
| 221 | + * @param string $action |
| 222 | + * @return bool |
| 223 | + * @access private |
| 224 | + */ |
| 225 | + function userCanPreview( $action ) { |
| 226 | + global $wgTitle, $wgRequest, $wgUser; |
| 227 | + |
| 228 | + if( $action != 'submit' ) |
| 229 | + return false; |
| 230 | + if( !$wgRequest->wasPosted() ) |
| 231 | + return false; |
| 232 | + if( !$wgTitle->userCanEditCssJsSubpage() ) |
| 233 | + return false; |
| 234 | + return $wgUser->matchEditToken( |
| 235 | + $wgRequest->getVal( 'wpEditToken' ) ); |
| 236 | + } |
| 237 | + |
214 | 238 | # get the user/site-specific stylesheet, SkinPHPTal called from RawPage.php (settings are cached that way) |
215 | 239 | function getUserStylesheet() { |
216 | 240 | global $wgOut, $wgStylePath, $wgContLang, $wgUser, $wgRequest, $wgTitle, $wgAllowUserCss; |
— | — | @@ -218,7 +242,7 @@ |
219 | 243 | $s = "@import \"$wgStylePath/$sheet\";\n"; |
220 | 244 | if($wgContLang->isRTL()) $s .= "@import \"$wgStylePath/common/common_rtl.css\";\n"; |
221 | 245 | if( $wgAllowUserCss && $wgUser->getID() != 0 ) { # logged in |
222 | | - if($wgTitle->isCssSubpage() and $action == 'submit' and $wgTitle->userCanEditCssJsSubpage()) { |
| 246 | + if($wgTitle->isCssSubpage() && $this->userCanPreview( $action ) ) { |
223 | 247 | $s .= $wgRequest->getText('wpTextbox1'); |
224 | 248 | } else { |
225 | 249 | $userpage = $wgContLang->getNsText( Namespace::getUser() ) . ":" . $wgUser->getName(); |
Index: branches/REL1_4/phase3/RELEASE-NOTES |
— | — | @@ -4,17 +4,47 @@ |
5 | 5 | setting since version 1.2.0. If you have it on, turn it *off* if you can. |
6 | 6 | |
7 | 7 | |
8 | | -== MediaWiki 1.4 BETA 5 == |
| 8 | +== MediaWiki 1.4 BETA 6 == |
9 | 9 | |
10 | | -MediaWiki 1.4beta5 is a security and bug fix release for the 1.4 beta |
11 | | -series. Previous MediaWiki 1.4 beta releases include an input validation |
| 10 | +MediaWiki 1.4beta6 is a security and bug fix release for the 1.4 beta |
| 11 | +series. |
| 12 | + |
| 13 | +An attacker could craft a URL which, when visited by a particular |
| 14 | +logged-in user, would execute arbitrary JavaScript code on the user's |
| 15 | +browser in the wiki's site context. This attack has been blocked, and as |
| 16 | +an extra precaution the user CSS and JavaScript subpage support is now |
| 17 | +disabled by default. Sites which want this ability may set $wgAllowUserCss |
| 18 | +and $wgAllowUserJs in LocalSettings.php. |
| 19 | + |
| 20 | +Additional protections have been added against off-site form submissions |
| 21 | +hijacking user credentials. Authors of bot tools may need to update their |
| 22 | +code to include additional fields. |
| 23 | + |
| 24 | +These problems affects the 1.3.x releases as well; 1.3 users should |
| 25 | +upgrade to 1.3.10. |
| 26 | + |
| 27 | +Note that 1.4 beta releases prior to beta 5 include an input validation |
12 | 28 | error which could lead to execution of arbitrary PHP code on the server. |
| 29 | +Users of older betas should upgrade immediately to the current version. |
13 | 30 | |
14 | | -All users of 1.4 beta releases are strongly urged to upgrade to 1.4beta5 |
15 | | -immediately. The 1.3.x stable release series is not affected by this |
16 | | -problem. |
17 | 31 | |
| 32 | +Beta 6 also introduces the use of rel="nofollow" attributes on external |
| 33 | +links in wiki pages to reduce the effectiveness of wiki spam. This will |
| 34 | +cause participating search engines to ignore external URL links from wiki |
| 35 | +pages for purposes of page relevancy ranking. |
18 | 36 | |
| 37 | +The current implementation adds this attribute to _all_ external URL |
| 38 | +links in wiki text (but not internal [[wiki links]] or interwiki links). |
| 39 | +To disable the attribute for _all_ external links, add this line to your |
| 40 | +LocalSettings.php: |
| 41 | + |
| 42 | + $wgNoFollowLinks = false |
| 43 | + |
| 44 | +For background information on nofollow see: |
| 45 | + |
| 46 | + http://www.google.com/googleblog/2005/01/preventing-comment-spam.html |
| 47 | + |
| 48 | + |
19 | 49 | ''''' Thinking of using MySQL 4.1? Please read this first! ''''' |
20 | 50 | ''''' Your PHP installation probably uses the OLD protocol ''''' |
21 | 51 | ''''' http://dev.mysql.com/doc/mysql/en/Old_client.html ''''' |
— | — | @@ -279,6 +309,13 @@ |
280 | 310 | * (bug 1435) Fixed many CSS errors |
281 | 311 | * (bug 1457) Fix XHTML validation on category column list |
282 | 312 | * (bug 1458) Don't save if edit form submission is incomplete |
| 313 | +* Logged-in edits and preview of user CSS/JS are now locked to a session token. |
| 314 | +* Per-user CSS and JavaScript subpage customizations now disabled by default. |
| 315 | + They can be re-enabled via $wgAllowUserJs and $wgAllowUserCss. |
| 316 | +* Removed .ogg from the default uploads whitelist as an extra precaution. |
| 317 | + If your web server is configured to serve Ogg files with the correct |
| 318 | + Content-Type header, you can re-add it in LocalSettings.php: |
| 319 | + $wgFileExtensions[] = 'ogg'; |
283 | 320 | |
284 | 321 | === Caveats === |
285 | 322 | |