r114429 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r114428‎ | r114429 | r114430 >
Date:19:43, 22 March 2012
Author:reedy
Status:deferred
Tags:
Comment:
* (bug 34212) ApiBlock/ApiUnblock allow action to take place without a token parameter present

* (bug 35317) CSRF in Special:Upload
Revert r56793, which removed the CSRF check for Special:Upload for normal file
uploads. Cross-site posting of file uploads without user interaction has been
possible since at least as early as Chrome 8 (late 2010) and Firefox 6 (mid
2011).

Commonist has used api.php since version 0.4.0 (April 2010), and the API
already requires an edit token, so Commonist 0.4.0+ is not affected by this
change.

* (bug 34907) Fix for CSRF vulnerability due to mw.user.tokens. Patch by Roan
Kattouw and Tim Starling.
* Filter out private modules early in ResourceLoader::makeResponse() and just
pretend they weren't specified. This means these modules cannot be loaded
through load.php . This filtering must not happen in makeModuleResponse(),
because that would break inlining.
* Force inlining of private modules in OutputPage::makeResourceLoaderLink(),
disregarding $wgResourceLoaderInlinePrivateModules
* Remove $wgResourceLoaderInlinePrivateModules
* Remove special treatment of private modules ($private) in
ResourceLoader::makeResponse() and sendResponseHeaders(), because we're not
allowing private modules to be loaded through here any more
* Remove identity checks in ResourceLoaderUserOptionsModule and
ResourceLoaderUserCSSPrefsModule, they didn't make a lot of sense before but
they're certainly useless now.
* Factored out error comment construction in ResourceLoader.php and stripped
comment terminations from exception messages. I didn't find an XSS
vulnerability but it looked scary.

* (bug 35315) XSS in CharInsert, forged strip markers
Modified paths:
  • /branches/wmf/1.19wmf1 (modified) (history)
  • /branches/wmf/1.19wmf1/extensions/CharInsert/CharInsert.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes (modified) (history)
  • /branches/wmf/1.19wmf1/includes/AutoLoader.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/CryptRand.php (added) (history)
  • /branches/wmf/1.19wmf1/includes/DefaultSettings.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/GlobalFunctions.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/OutputPage.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/User.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/api/ApiMain.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/installer/Installer.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/resourceloader/ResourceLoader.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/resourceloader/ResourceLoaderUserOptionsModule.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/specials (modified) (history)
  • /branches/wmf/1.19wmf1/includes/specials/SpecialUpload.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/specials/SpecialUserlogin.php (modified) (history)
  • /branches/wmf/1.19wmf1/includes/specials/SpecialWatchlist.php (modified) (history)

Diff [purge]

Index: branches/wmf/1.19wmf1/extensions/CharInsert/CharInsert.php
@@ -49,7 +49,8 @@
5050 return true;
5151 }
5252
53 -function charInsert( $data ) {
 53+function charInsert( $data, $params, $parser ) {
 54+ $data = $parser->mStripState->unstripBoth( $data );
5455 return implode( "<br />\n",
5556 array_map( 'charInsertLine',
5657 explode( "\n", trim( $data ) ) ) );
Index: branches/wmf/1.19wmf1/includes/CryptRand.php
@@ -0,0 +1,463 @@
 2+<?php
 3+/**
 4+ * A cryptographic random generator class used for generating secret keys
 5+ *
 6+ * This is based in part on Drupal code as well as what we used in our own code
 7+ * prior to introduction of this class.
 8+ *
 9+ * @author Daniel Friesen
 10+ * @file
 11+ */
 12+
 13+class MWCryptRand {
 14+
 15+ /**
 16+ * Minimum number of iterations we want to make in our drift calculations.
 17+ */
 18+ const MIN_ITERATIONS = 1000;
 19+
 20+ /**
 21+ * Number of milliseconds we want to spend generating each separate byte
 22+ * of the final generated bytes.
 23+ * This is used in combination with the hash length to determine the duration
 24+ * we should spend doing drift calculations.
 25+ */
 26+ const MSEC_PER_BYTE = 0.5;
 27+
 28+ /**
 29+ * Singleton instance for public use
 30+ */
 31+ protected static $singleton = null;
 32+
 33+ /**
 34+ * The hash algorithm being used
 35+ */
 36+ protected $algo = null;
 37+
 38+ /**
 39+ * The number of bytes outputted by the hash algorithm
 40+ */
 41+ protected $hashLength = null;
 42+
 43+ /**
 44+ * A boolean indicating whether the previous random generation was done using
 45+ * cryptographically strong random number generator or not.
 46+ */
 47+ protected $strong = null;
 48+
 49+ /**
 50+ * Initialize an initial random state based off of whatever we can find
 51+ */
 52+ protected function initialRandomState() {
 53+ // $_SERVER contains a variety of unstable user and system specific information
 54+ // It'll vary a little with each page, and vary even more with separate users
 55+ // It'll also vary slightly across different machines
 56+ $state = serialize( $_SERVER );
 57+
 58+ // To try and vary the system information of the state a bit more
 59+ // by including the system's hostname into the state
 60+ $state .= wfHostname();
 61+
 62+ // Try to gather a little entropy from the different php rand sources
 63+ $state .= rand() . uniqid( mt_rand(), true );
 64+
 65+ // Include some information about the filesystem's current state in the random state
 66+ $files = array();
 67+ // We know this file is here so grab some info about ourself
 68+ $files[] = __FILE__;
 69+ // The config file is likely the most often edited file we know should be around
 70+ // so if the constant with it's location is defined include it's stat info into the state
 71+ if ( defined( 'MW_CONFIG_FILE' ) ) {
 72+ $files[] = MW_CONFIG_FILE;
 73+ }
 74+ foreach ( $files as $file ) {
 75+ wfSuppressWarnings();
 76+ $stat = stat( $file );
 77+ wfRestoreWarnings();
 78+ if ( $stat ) {
 79+ // stat() duplicates data into numeric and string keys so kill off all the numeric ones
 80+ foreach ( $stat as $k => $v ) {
 81+ if ( is_numeric( $k ) ) {
 82+ unset( $k );
 83+ }
 84+ }
 85+ // The absolute filename itself will differ from install to install so don't leave it out
 86+ $state .= realpath( $file );
 87+ $state .= implode( '', $stat );
 88+ } else {
 89+ // The fact that the file isn't there is worth at least a
 90+ // minuscule amount of entropy.
 91+ $state .= '0';
 92+ }
 93+ }
 94+
 95+ // Try and make this a little more unstable by including the varying process
 96+ // id of the php process we are running inside of if we are able to access it
 97+ if ( function_exists( 'getmypid' ) ) {
 98+ $state .= getmypid();
 99+ }
 100+
 101+ // If available try to increase the instability of the data by throwing in
 102+ // the precise amount of memory that we happen to be using at the moment.
 103+ if ( function_exists( 'memory_get_usage' ) ) {
 104+ $state .= memory_get_usage( true );
 105+ }
 106+
 107+ // It's mostly worthless but throw the wiki's id into the data for a little more variance
 108+ $state .= wfWikiID();
 109+
 110+ // If we have a secret key or proxy key set then throw it into the state as well
 111+ global $wgSecretKey, $wgProxyKey;
 112+ if ( $wgSecretKey ) {
 113+ $state .= $wgSecretKey;
 114+ } elseif ( $wgProxyKey ) {
 115+ $state .= $wgProxyKey;
 116+ }
 117+
 118+ return $state;
 119+ }
 120+
 121+ /**
 122+ * Randomly hash data while mixing in clock drift data for randomness
 123+ *
 124+ * @param $data The data to randomly hash.
 125+ * @return String The hashed bytes
 126+ * @author Tim Starling
 127+ */
 128+ protected function driftHash( $data ) {
 129+ // Minimum number of iterations (to avoid slow operations causing the loop to gather little entropy)
 130+ $minIterations = self::MIN_ITERATIONS;
 131+ // Duration of time to spend doing calculations (in seconds)
 132+ $duration = ( self::MSEC_PER_BYTE / 1000 ) * $this->hashLength();
 133+ // Create a buffer to use to trigger memory operations
 134+ $bufLength = 10000000;
 135+ $buffer = str_repeat( ' ', $bufLength );
 136+ $bufPos = 0;
 137+
 138+ // Iterate for $duration seconds or at least $minIerations number of iterations
 139+ $iterations = 0;
 140+ $startTime = microtime( true );
 141+ $currentTime = $startTime;
 142+ while ( $iterations < $minIterations || $currentTime - $startTime < $duration ) {
 143+ // Trigger some memory writing to trigger some bus activity
 144+ // This may create variance in the time between iterations
 145+ $bufPos = ( $bufPos + 13 ) % $bufLength;
 146+ $buffer[$bufPos] = ' ';
 147+ // Add the drift between this iteration and the last in as entropy
 148+ $nextTime = microtime( true );
 149+ $delta = (int)( ( $nextTime - $currentTime ) * 1000000 );
 150+ $data .= $delta;
 151+ // Every 100 iterations hash the data and entropy
 152+ if ( $iterations % 100 === 0 ) {
 153+ $data = sha1( $data );
 154+ }
 155+ $currentTime = $nextTime;
 156+ $iterations++;
 157+ }
 158+ $timeTaken = $currentTime - $startTime;
 159+ $data = $this->hash( $data );
 160+
 161+ wfDebug( __METHOD__ . ": Clock drift calculation " .
 162+ "(time-taken=" . ( $timeTaken * 1000 ) . "ms, " .
 163+ "iterations=$iterations, " .
 164+ "time-per-iteration=" . ( $timeTaken / $iterations * 1e6 ) . "us)\n" );
 165+ return $data;
 166+ }
 167+
 168+ /**
 169+ * Return a rolling random state initially build using data from unstable sources
 170+ * @return A new weak random state
 171+ */
 172+ protected function randomState() {
 173+ static $state = null;
 174+ if ( is_null( $state ) ) {
 175+ // Initialize the state with whatever unstable data we can find
 176+ // It's important that this data is hashed right afterwards to prevent
 177+ // it from being leaked into the output stream
 178+ $state = $this->hash( $this->initialRandomState() );
 179+ }
 180+ // Generate a new random state based on the initial random state or previous
 181+ // random state by combining it with clock drift
 182+ $state = $this->driftHash( $state );
 183+ return $state;
 184+ }
 185+
 186+ /**
 187+ * Decide on the best acceptable hash algorithm we have available for hash()
 188+ * @return String A hash algorithm
 189+ */
 190+ protected function hashAlgo() {
 191+ if ( !is_null( $this->algo ) ) {
 192+ return $this->algo;
 193+ }
 194+
 195+ $algos = hash_algos();
 196+ $preference = array( 'whirlpool', 'sha256', 'sha1', 'md5' );
 197+
 198+ foreach ( $preference as $algorithm ) {
 199+ if ( in_array( $algorithm, $algos ) ) {
 200+ $this->algo = $algorithm;
 201+ wfDebug( __METHOD__ . ": Using the {$this->algo} hash algorithm.\n" );
 202+ return $this->algo;
 203+ }
 204+ }
 205+
 206+ // We only reach here if no acceptable hash is found in the list, this should
 207+ // be a technical impossibility since most of php's hash list is fixed and
 208+ // some of the ones we list are available as their own native functions
 209+ // But since we already require at least 5.2 and hash() was default in
 210+ // 5.1.2 we don't bother falling back to methods like sha1 and md5.
 211+ throw new MWException( "Could not find an acceptable hashing function in hash_algos()" );
 212+ }
 213+
 214+ /**
 215+ * Return the byte-length output of the hash algorithm we are
 216+ * using in self::hash and self::hmac.
 217+ *
 218+ * @return int Number of bytes the hash outputs
 219+ */
 220+ protected function hashLength() {
 221+ if ( is_null( $this->hashLength ) ) {
 222+ $this->hashLength = strlen( $this->hash( '' ) );
 223+ }
 224+ return $this->hashLength;
 225+ }
 226+
 227+ /**
 228+ * Generate an acceptably unstable one-way-hash of some text
 229+ * making use of the best hash algorithm that we have available.
 230+ *
 231+ * @return String A raw hash of the data
 232+ */
 233+ protected function hash( $data ) {
 234+ return hash( $this->hashAlgo(), $data, true );
 235+ }
 236+
 237+ /**
 238+ * Generate an acceptably unstable one-way-hmac of some text
 239+ * making use of the best hash algorithm that we have available.
 240+ *
 241+ * @return String A raw hash of the data
 242+ */
 243+ protected function hmac( $data, $key ) {
 244+ return hash_hmac( $this->hashAlgo(), $data, $key, true );
 245+ }
 246+
 247+ /**
 248+ * @see self::wasStrong()
 249+ */
 250+ public function realWasStrong() {
 251+ if ( is_null( $this->strong ) ) {
 252+ throw new MWException( __METHOD__ . ' called before generation of random data' );
 253+ }
 254+ return $this->strong;
 255+ }
 256+
 257+ /**
 258+ * @see self::generate()
 259+ */
 260+ public function realGenerate( $bytes, $forceStrong = false ) {
 261+ wfProfileIn( __METHOD__ );
 262+
 263+ wfDebug( __METHOD__ . ": Generating cryptographic random bytes for " . wfGetAllCallers( 5 ) . "\n" );
 264+
 265+ $bytes = floor( $bytes );
 266+ static $buffer = '';
 267+ if ( is_null( $this->strong ) ) {
 268+ // Set strength to false initially until we know what source data is coming from
 269+ $this->strong = true;
 270+ }
 271+
 272+ if ( strlen( $buffer ) < $bytes ) {
 273+ // If available make use of mcrypt_create_iv URANDOM source to generate randomness
 274+ // On unix-like systems this reads from /dev/urandom but does it without any buffering
 275+ // and bypasses openbasdir restrictions so it's preferable to reading directly
 276+ // On Windows starting in PHP 5.3.0 Windows' native CryptGenRandom is used to generate
 277+ // entropy so this is also preferable to just trying to read urandom because it may work
 278+ // on Windows systems as well.
 279+ if ( function_exists( 'mcrypt_create_iv' ) ) {
 280+ wfProfileIn( __METHOD__ . '-mcrypt' );
 281+ $rem = $bytes - strlen( $buffer );
 282+ $iv = mcrypt_create_iv( $rem, MCRYPT_DEV_URANDOM );
 283+ if ( $iv === false ) {
 284+ wfDebug( __METHOD__ . ": mcrypt_create_iv returned false.\n" );
 285+ } else {
 286+ $bytes .= $iv;
 287+ wfDebug( __METHOD__ . ": mcrypt_create_iv generated " . strlen( $iv ) . " bytes of randomness.\n" );
 288+ }
 289+ wfProfileOut( __METHOD__ . '-mcrypt' );
 290+ }
 291+ }
 292+
 293+ if ( strlen( $buffer ) < $bytes ) {
 294+ // If available make use of openssl's random_pesudo_bytes method to attempt to generate randomness.
 295+ // However don't do this on Windows with PHP < 5.3.4 due to a bug:
 296+ // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
 297+ if ( function_exists( 'openssl_random_pseudo_bytes' )
 298+ && ( !wfIsWindows() || version_compare( PHP_VERSION, '5.3.4', '>=' ) )
 299+ ) {
 300+ wfProfileIn( __METHOD__ . '-openssl' );
 301+ $rem = $bytes - strlen( $buffer );
 302+ $openssl_bytes = openssl_random_pseudo_bytes( $rem, $openssl_strong );
 303+ if ( $openssl_bytes === false ) {
 304+ wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes returned false.\n" );
 305+ } else {
 306+ $buffer .= $openssl_bytes;
 307+ wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes generated " . strlen( $openssl_bytes ) . " bytes of " . ( $openssl_strong ? "strong" : "weak" ) . " randomness.\n" );
 308+ }
 309+ if ( strlen( $buffer ) >= $bytes ) {
 310+ // openssl tells us if the random source was strong, if some of our data was generated
 311+ // using it use it's say on whether the randomness is strong
 312+ $this->strong = !!$openssl_strong;
 313+ }
 314+ wfProfileOut( __METHOD__ . '-openssl' );
 315+ }
 316+ }
 317+
 318+ // Only read from urandom if we can control the buffer size or were passed forceStrong
 319+ if ( strlen( $buffer ) < $bytes && ( function_exists( 'stream_set_read_buffer' ) || $forceStrong ) ) {
 320+ wfProfileIn( __METHOD__ . '-fopen-urandom' );
 321+ $rem = $bytes - strlen( $buffer );
 322+ if ( !function_exists( 'stream_set_read_buffer' ) && $forceStrong ) {
 323+ wfDebug( __METHOD__ . ": Was forced to read from /dev/urandom without control over the buffer size.\n" );
 324+ }
 325+ // /dev/urandom is generally considered the best possible commonly
 326+ // available random source, and is available on most *nix systems.
 327+ wfSuppressWarnings();
 328+ $urandom = fopen( "/dev/urandom", "rb" );
 329+ wfRestoreWarnings();
 330+
 331+ // Attempt to read all our random data from urandom
 332+ // php's fread always does buffered reads based on the stream's chunk_size
 333+ // so in reality it will usually read more than the amount of data we're
 334+ // asked for and not storing that risks depleting the system's random pool.
 335+ // If stream_set_read_buffer is available set the chunk_size to the amount
 336+ // of data we need. Otherwise read 8k, php's default chunk_size.
 337+ if ( $urandom ) {
 338+ // php's default chunk_size is 8k
 339+ $chunk_size = 1024 * 8;
 340+ if ( function_exists( 'stream_set_read_buffer' ) ) {
 341+ // If possible set the chunk_size to the amount of data we need
 342+ stream_set_read_buffer( $urandom, $rem );
 343+ $chunk_size = $rem;
 344+ }
 345+ $random_bytes = fread( $urandom, max( $chunk_size, $rem ) );
 346+ $buffer .= $random_bytes;
 347+ fclose( $urandom );
 348+ wfDebug( __METHOD__ . ": /dev/urandom generated " . strlen( $random_bytes ) . " bytes of randomness.\n" );
 349+ if ( strlen( $buffer ) >= $bytes ) {
 350+ // urandom is always strong, set to true if all our data was generated using it
 351+ $this->strong = true;
 352+ }
 353+ } else {
 354+ wfDebug( __METHOD__ . ": /dev/urandom could not be opened.\n" );
 355+ }
 356+ wfProfileOut( __METHOD__ . '-fopen-urandom' );
 357+ }
 358+
 359+ // If we cannot use or generate enough data from a secure source
 360+ // use this loop to generate a good set of pseudo random data.
 361+ // This works by initializing a random state using a pile of unstable data
 362+ // and continually shoving it through a hash along with a variable salt.
 363+ // We hash the random state with more salt to avoid the state from leaking
 364+ // out and being used to predict the /randomness/ that follows.
 365+ if ( strlen( $buffer ) < $bytes ) {
 366+ wfDebug( __METHOD__ . ": Falling back to using a pseudo random state to generate randomness.\n" );
 367+ }
 368+ while ( strlen( $buffer ) < $bytes ) {
 369+ wfProfileIn( __METHOD__ . '-fallback' );
 370+ $buffer .= $this->hmac( $this->randomState(), mt_rand() );
 371+ // This code is never really cryptographically strong, if we use it
 372+ // at all, then set strong to false.
 373+ $this->strong = false;
 374+ wfProfileOut( __METHOD__ . '-fallback' );
 375+ }
 376+
 377+ // Once the buffer has been filled up with enough random data to fulfill
 378+ // the request shift off enough data to handle the request and leave the
 379+ // unused portion left inside the buffer for the next request for random data
 380+ $generated = substr( $buffer, 0, $bytes );
 381+ $buffer = substr( $buffer, $bytes );
 382+
 383+ wfDebug( __METHOD__ . ": " . strlen( $buffer ) . " bytes of randomness leftover in the buffer.\n" );
 384+
 385+ wfProfileOut( __METHOD__ );
 386+ return $generated;
 387+ }
 388+
 389+ /**
 390+ * @see self::generateHex()
 391+ */
 392+ public function realGenerateHex( $chars, $forceStrong = false ) {
 393+ // hex strings are 2x the length of raw binary so we divide the length in half
 394+ // odd numbers will result in a .5 that leads the generate() being 1 character
 395+ // short, so we use ceil() to ensure that we always have enough bytes
 396+ $bytes = ceil( $chars / 2 );
 397+ // Generate the data and then convert it to a hex string
 398+ $hex = bin2hex( $this->generate( $bytes, $forceStrong ) );
 399+ // A bit of paranoia here, the caller asked for a specific length of string
 400+ // here, and it's possible (eg when given an odd number) that we may actually
 401+ // have at least 1 char more than they asked for. Just in case they made this
 402+ // call intending to insert it into a database that does truncation we don't
 403+ // want to give them too much and end up with their database and their live
 404+ // code having two different values because part of what we gave them is truncated
 405+ // hence, we strip out any run of characters longer than what we were asked for.
 406+ return substr( $hex, 0, $chars );
 407+ }
 408+
 409+ /** Publicly exposed static methods **/
 410+
 411+ /**
 412+ * Return a singleton instance of MWCryptRand
 413+ */
 414+ protected static function singleton() {
 415+ if ( is_null( self::$singleton ) ) {
 416+ self::$singleton = new self;
 417+ }
 418+ return self::$singleton;
 419+ }
 420+
 421+ /**
 422+ * Return a boolean indicating whether or not the source used for cryptographic
 423+ * random bytes generation in the previously run generate* call
 424+ * was cryptographically strong.
 425+ *
 426+ * @return bool Returns true if the source was strong, false if not.
 427+ */
 428+ public static function wasStrong() {
 429+ return self::singleton()->realWasStrong();
 430+ }
 431+
 432+ /**
 433+ * Generate a run of (ideally) cryptographically random data and return
 434+ * it in raw binary form.
 435+ * You can use MWCryptRand::wasStrong() if you wish to know if the source used
 436+ * was cryptographically strong.
 437+ *
 438+ * @param $bytes int the number of bytes of random data to generate
 439+ * @param $forceStrong bool Pass true if you want generate to prefer cryptographically
 440+ * strong sources of entropy even if reading from them may steal
 441+ * more entropy from the system than optimal.
 442+ * @return String Raw binary random data
 443+ */
 444+ public static function generate( $bytes, $forceStrong = false ) {
 445+ return self::singleton()->realGenerate( $bytes, $forceStrong );
 446+ }
 447+
 448+ /**
 449+ * Generate a run of (ideally) cryptographically random data and return
 450+ * it in hexadecimal string format.
 451+ * You can use MWCryptRand::wasStrong() if you wish to know if the source used
 452+ * was cryptographically strong.
 453+ *
 454+ * @param $chars int the number of hex chars of random data to generate
 455+ * @param $forceStrong bool Pass true if you want generate to prefer cryptographically
 456+ * strong sources of entropy even if reading from them may steal
 457+ * more entropy from the system than optimal.
 458+ * @return String Hexadecimal random data
 459+ */
 460+ public static function generateHex( $chars, $forceStrong = false ) {
 461+ return self::singleton()->realGenerateHex( $chars, $forceStrong );
 462+ }
 463+
 464+}
Index: branches/wmf/1.19wmf1/includes/User.php
@@ -831,23 +831,20 @@
832832 }
833833
834834 /**
835 - * Return a random password. Sourced from mt_rand, so it's not particularly secure.
836 - * @todo hash random numbers to improve security, like generateToken()
 835+ * Return a random password.
837836 *
838837 * @return String new random password
839838 */
840839 public static function randomPassword() {
841840 global $wgMinimalPasswordLength;
842 - $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
843 - $l = strlen( $pwchars ) - 1;
844 -
845 - $pwlength = max( 7, $wgMinimalPasswordLength );
846 - $digit = mt_rand( 0, $pwlength - 1 );
847 - $np = '';
848 - for ( $i = 0; $i < $pwlength; $i++ ) {
849 - $np .= $i == $digit ? chr( mt_rand( 48, 57 ) ) : $pwchars[ mt_rand( 0, $l ) ];
850 - }
851 - return $np;
 841+ // Decide the final password length based on our min password length, stopping at a minimum of 10 chars
 842+ $length = max( 10, $wgMinimalPasswordLength );
 843+ // Multiply by 1.25 to get the number of hex characters we need
 844+ $length = $length * 1.25;
 845+ // Generate random hex chars
 846+ $hex = MWCryptRand::generateHex( $length );
 847+ // Convert from base 16 to base 32 to get a proper password like string
 848+ return wfBaseConvert( $hex, 16, 32 );
852849 }
853850
854851 /**
@@ -877,7 +874,7 @@
878875 $this->mTouched = '0'; # Allow any pages to be cached
879876 }
880877
881 - $this->setToken(); # Random
 878+ $this->mToken = null; // Don't run cryptographic functions till we need a token
882879 $this->mEmailAuthenticated = null;
883880 $this->mEmailToken = '';
884881 $this->mEmailTokenExpires = null;
@@ -984,11 +981,11 @@
985982 return false;
986983 }
987984
988 - if ( $request->getSessionData( 'wsToken' ) !== null ) {
989 - $passwordCorrect = $proposedUser->getToken() === $request->getSessionData( 'wsToken' );
 985+ if ( $request->getSessionData( 'wsToken' ) ) {
 986+ $passwordCorrect = $proposedUser->getToken( false ) === $request->getSessionData( 'wsToken' );
990987 $from = 'session';
991 - } elseif ( $request->getCookie( 'Token' ) !== null ) {
992 - $passwordCorrect = $proposedUser->getToken() === $request->getCookie( 'Token' );
 988+ } elseif ( $request->getCookie( 'Token' ) ) {
 989+ $passwordCorrect = $proposedUser->getToken( false ) === $request->getCookie( 'Token' );
993990 $from = 'cookie';
994991 } else {
995992 # No session or persistent login cookie
@@ -1093,6 +1090,9 @@
10941091 }
10951092 $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
10961093 $this->mToken = $row->user_token;
 1094+ if ( $this->mToken == '' ) {
 1095+ $this->mToken = null;
 1096+ }
10971097 $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
10981098 $this->mEmailToken = $row->user_email_token;
10991099 $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
@@ -2015,10 +2015,14 @@
20162016
20172017 /**
20182018 * Get the user's current token.
 2019+ * @param $forceCreation Force the generation of a new token if the user doesn't have one (default=true for backwards compatibility)
20192020 * @return String Token
20202021 */
2021 - public function getToken() {
 2022+ public function getToken( $forceCreation = true ) {
20222023 $this->load();
 2024+ if ( !$this->mToken && $forceCreation ) {
 2025+ $this->setToken();
 2026+ }
20232027 return $this->mToken;
20242028 }
20252029
@@ -2032,14 +2036,7 @@
20332037 global $wgSecretKey, $wgProxyKey;
20342038 $this->load();
20352039 if ( !$token ) {
2036 - if ( $wgSecretKey ) {
2037 - $key = $wgSecretKey;
2038 - } elseif ( $wgProxyKey ) {
2039 - $key = $wgProxyKey;
2040 - } else {
2041 - $key = microtime();
2042 - }
2043 - $this->mToken = md5( $key . mt_rand( 0, 0x7fffffff ) . wfWikiID() . $this->mId );
 2040+ $this->mToken = MWCryptRand::generateHex( USER_TOKEN_LENGTH );
20442041 } else {
20452042 $this->mToken = $token;
20462043 }
@@ -2745,6 +2742,14 @@
27462743
27472744 $this->load();
27482745 if ( 0 == $this->mId ) return;
 2746+ if ( !$this->mToken ) {
 2747+ // When token is empty or NULL generate a new one and then save it to the database
 2748+ // This allows a wiki to re-secure itself after a leak of it's user table or $wgSecretKey
 2749+ // Simply by setting every cell in the user_token column to NULL and letting them be
 2750+ // regenerated as users log back into the wiki.
 2751+ $this->setToken();
 2752+ $this->saveSettings();
 2753+ }
27492754 $session = array(
27502755 'wsUserID' => $this->mId,
27512756 'wsToken' => $this->mToken,
@@ -2821,7 +2826,7 @@
28222827 'user_email' => $this->mEmail,
28232828 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
28242829 'user_touched' => $dbw->timestamp( $this->mTouched ),
2825 - 'user_token' => $this->mToken,
 2830+ 'user_token' => strval( $this->mToken ),
28262831 'user_email_token' => $this->mEmailToken,
28272832 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
28282833 ), array( /* WHERE */
@@ -2887,7 +2892,7 @@
28882893 'user_email' => $user->mEmail,
28892894 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
28902895 'user_real_name' => $user->mRealName,
2891 - 'user_token' => $user->mToken,
 2896+ 'user_token' => strval( $user->mToken ),
28922897 'user_registration' => $dbw->timestamp( $user->mRegistration ),
28932898 'user_editcount' => 0,
28942899 );
@@ -2920,7 +2925,7 @@
29212926 'user_email' => $this->mEmail,
29222927 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
29232928 'user_real_name' => $this->mRealName,
2924 - 'user_token' => $this->mToken,
 2929+ 'user_token' => strval( $this->mToken ),
29252930 'user_registration' => $dbw->timestamp( $this->mRegistration ),
29262931 'user_editcount' => 0,
29272932 ), __METHOD__
@@ -3184,7 +3189,7 @@
31853190 } else {
31863191 $token = $request->getSessionData( 'wsEditToken' );
31873192 if ( $token === null ) {
3188 - $token = self::generateToken();
 3193+ $token = MWCryptRand::generateHex( 32 );
31893194 $request->setSessionData( 'wsEditToken', $token );
31903195 }
31913196 if( is_array( $salt ) ) {
@@ -3201,8 +3206,7 @@
32023207 * @return String The new random token
32033208 */
32043209 public static function generateToken( $salt = '' ) {
3205 - $token = dechex( mt_rand() ) . dechex( mt_rand() );
3206 - return md5( $token . $salt );
 3210+ return MWCryptRand::generateHex( 32 );
32073211 }
32083212
32093213 /**
@@ -3308,12 +3312,11 @@
33093313 global $wgUserEmailConfirmationTokenExpiry;
33103314 $now = time();
33113315 $expires = $now + $wgUserEmailConfirmationTokenExpiry;
3312 - $expiration = wfTimestamp( TS_MW, $expires );
3313 - $token = self::generateToken( $this->mId . $this->mEmail . $expires );
 3316+ $this->load();
 3317+ $token = MWCryptRand::generateHex( 32 );
33143318 $hash = md5( $token );
3315 - $this->load();
33163319 $this->mEmailToken = $hash;
3317 - $this->mEmailTokenExpires = $expiration;
 3320+ $this->mEmailTokenExpires = wfTimestamp( TS_MW, $expires );
33183321 return $token;
33193322 }
33203323
@@ -3862,7 +3865,7 @@
38633866
38643867 if( $wgPasswordSalt ) {
38653868 if ( $salt === false ) {
3866 - $salt = substr( wfGenerateToken(), 0, 8 );
 3869+ $salt = MWCryptRand::generateHex( 8 );
38673870 }
38683871 return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) );
38693872 } else {
Property changes on: branches/wmf/1.19wmf1/includes/User.php
___________________________________________________________________
Modified: svn:mergeinfo
38703873 Merged /branches/REL1_19/phase3/includes/User.php:r114241,114283,114355
Index: branches/wmf/1.19wmf1/includes/GlobalFunctions.php
@@ -3269,6 +3269,33 @@
32703270 }
32713271
32723272 /**
 3273+ * Override session_id before session startup if php's built-in
 3274+ * session generation code is not secure.
 3275+ */
 3276+function wfFixSessionID() {
 3277+ // If the cookie or session id is already set we already have a session and should abort
 3278+ if ( isset( $_COOKIE[ session_name() ] ) || session_id() ) {
 3279+ return;
 3280+ }
 3281+
 3282+ // PHP's built-in session entropy is enabled if:
 3283+ // - entropy_file is set or you're on Windows with php 5.3.3+
 3284+ // - AND entropy_length is > 0
 3285+ // We treat it as disabled if it doesn't have an entropy length of at least 32
 3286+ $entropyEnabled = (
 3287+ ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) )
 3288+ || ini_get( 'session.entropy_file' )
 3289+ )
 3290+ && intval( ini_get( 'session.entropy_length' ) ) >= 32;
 3291+
 3292+ // If built-in entropy is not enabled or not sufficient override php's built in session id generation code
 3293+ if ( !$entropyEnabled ) {
 3294+ wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, overriding session id generation using our cryptrand source.\n" );
 3295+ session_id( MWCryptRand::generateHex( 32 ) );
 3296+ }
 3297+}
 3298+
 3299+/**
32733300 * Initialise php session
32743301 *
32753302 * @param $sessionId Bool
@@ -3307,6 +3334,8 @@
33083335 session_cache_limiter( 'private, must-revalidate' );
33093336 if ( $sessionId ) {
33103337 session_id( $sessionId );
 3338+ } else {
 3339+ wfFixSessionID();
33113340 }
33123341 wfSuppressWarnings();
33133342 session_start();
Property changes on: branches/wmf/1.19wmf1/includes/GlobalFunctions.php
___________________________________________________________________
Modified: svn:mergeinfo
33143343 Merged /branches/REL1_19/phase3/includes/GlobalFunctions.php:r114241,114283,114355
33153344 Merged /trunk/phase3/includes/GlobalFunctions.php:r114354
Index: branches/wmf/1.19wmf1/includes/OutputPage.php
@@ -2505,7 +2505,7 @@
25062506 * @return string html <script> and <style> tags
25072507 */
25082508 protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) {
2509 - global $wgResourceLoaderUseESI, $wgResourceLoaderInlinePrivateModules;
 2509+ global $wgResourceLoaderUseESI;
25102510
25112511 if ( !count( $modules ) ) {
25122512 return '';
@@ -2584,10 +2584,11 @@
25852585 continue;
25862586 }
25872587
2588 - // Support inlining of private modules if configured as such. Note that these
2589 - // modules should be loaded from getHeadScripts() before the first loader call.
2590 - // Otherwise other modules can't properly use them as dependencies (bug 30914)
2591 - if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) {
 2588+ // Inline private modules. These can't be loaded through load.php for security
 2589+ // reasons, see bug 34907. Note that these modules should be loaded from
 2590+ // getHeadScripts() before the first loader call. Otherwise other modules can't
 2591+ // properly use them as dependencies (bug 30914)
 2592+ if ( $group === 'private' ) {
25922593 if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
25932594 $links .= Html::inlineStyle(
25942595 $resourceLoader->makeModuleResponse( $context, $modules )
Index: branches/wmf/1.19wmf1/includes/installer/Installer.php
@@ -1405,8 +1405,7 @@
14061406 }
14071407
14081408 /**
1409 - * Generate $wgSecretKey. Will warn if we had to use mt_rand() instead of
1410 - * /dev/urandom
 1409+ * Generate $wgSecretKey. Will warn if we had to use an insecure random source.
14111410 *
14121411 * @return Status
14131412 */
@@ -1419,8 +1418,8 @@
14201419 }
14211420
14221421 /**
1423 - * Generate a secret value for variables using either
1424 - * /dev/urandom or mt_rand(). Produce a warning in the later case.
 1422+ * Generate a secret value for variables using our CryptRand generator.
 1423+ * Produce a warning if the random source was insecure.
14251424 *
14261425 * @param $keys Array
14271426 * @return Status
@@ -1428,28 +1427,18 @@
14291428 protected function doGenerateKeys( $keys ) {
14301429 $status = Status::newGood();
14311430
1432 - wfSuppressWarnings();
1433 - $file = fopen( "/dev/urandom", "r" );
1434 - wfRestoreWarnings();
1435 -
 1431+ $strong = true;
14361432 foreach ( $keys as $name => $length ) {
1437 - if ( $file ) {
1438 - $secretKey = bin2hex( fread( $file, $length / 2 ) );
1439 - } else {
1440 - $secretKey = '';
1441 -
1442 - for ( $i = 0; $i < $length / 8; $i++ ) {
1443 - $secretKey .= dechex( mt_rand( 0, 0x7fffffff ) );
1444 - }
 1433+ $secretKey = MWCryptRand::generateHex( $length, true );
 1434+ if ( !MWCryptRand::wasStrong() ) {
 1435+ $strong = false;
14451436 }
14461437
14471438 $this->setVar( $name, $secretKey );
14481439 }
14491440
1450 - if ( $file ) {
1451 - fclose( $file );
1452 - } else {
1453 - $names = array_keys ( $keys );
 1441+ if ( !$strong ) {
 1442+ $names = array_keys( $keys );
14541443 $names = preg_replace( '/^(.*)$/', '\$$1', $names );
14551444 global $wgLang;
14561445 $status->warning( 'config-insecure-keys', $wgLang->listToText( $names ), count( $names ) );
Index: branches/wmf/1.19wmf1/includes/api/ApiMain.php
@@ -595,7 +595,7 @@
596596
597597 // Die if token required, but not provided (unless there is a gettoken parameter)
598598 $salt = $module->getTokenSalt();
599 - if ( $salt !== false && !isset( $moduleParams['gettoken'] ) ) {
 599+ if ( $salt !== false && !$moduleParams['gettoken'] ) {
600600 if ( !isset( $moduleParams['token'] ) ) {
601601 $this->dieUsageMsg( array( 'missingparam', 'token' ) );
602602 } else {
Index: branches/wmf/1.19wmf1/includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php
@@ -44,41 +44,18 @@
4545 }
4646
4747 global $wgUser;
48 -
49 - if ( $context->getUser() === $wgUser->getName() ) {
50 - return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
51 - } else {
52 - return 1;
53 - }
 48+ return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
5449 }
55 -
56 - /**
57 - * Fetch the context's user options, or if it doesn't match current user,
58 - * the default options.
59 - *
60 - * @param $context ResourceLoaderContext: Context object
61 - * @return Array: List of user options keyed by option name
62 - */
63 - protected function contextUserOptions( ResourceLoaderContext $context ) {
64 - global $wgUser;
65 -
66 - // Verify identity -- this is a private module
67 - if ( $context->getUser() === $wgUser->getName() ) {
68 - return $wgUser->getOptions();
69 - } else {
70 - return User::getDefaultOptions();
71 - }
72 - }
7350
7451 /**
7552 * @param $context ResourceLoaderContext
7653 * @return array
7754 */
7855 public function getStyles( ResourceLoaderContext $context ) {
79 - global $wgAllowUserCssPrefs;
 56+ global $wgAllowUserCssPrefs, $wgUser;
8057
8158 if ( $wgAllowUserCssPrefs ) {
82 - $options = $this->contextUserOptions( $context );
 59+ $options = $wgUser->getOptions();
8360
8461 // Build CSS rules
8562 $rules = array();
Index: branches/wmf/1.19wmf1/includes/resourceloader/ResourceLoader.php
@@ -173,7 +173,7 @@
174174 $cache->set( $key, $result );
175175 } catch ( Exception $exception ) {
176176 // Return exception as a comment
177 - $result = "/*\n{$exception->__toString()}\n*/\n";
 177+ $result = $this->makeComment( $exception->__toString() );
178178 }
179179
180180 wfProfileOut( __METHOD__ );
@@ -430,13 +430,20 @@
431431 ob_start();
432432
433433 wfProfileIn( __METHOD__ );
434 - $exceptions = '';
 434+ $errors = '';
435435
436436 // Split requested modules into two groups, modules and missing
437437 $modules = array();
438438 $missing = array();
439439 foreach ( $context->getModules() as $name ) {
440440 if ( isset( $this->moduleInfos[$name] ) ) {
 441+ $module = $this->getModule( $name );
 442+ // Do not allow private modules to be loaded from the web.
 443+ // This is a security issue, see bug 34907.
 444+ if ( $module->getGroup() === 'private' ) {
 445+ $errors .= $this->makeComment( "Cannot show private module \"$name\"" );
 446+ continue;
 447+ }
441448 $modules[$name] = $this->getModule( $name );
442449 } else {
443450 $missing[] = $name;
@@ -448,12 +455,11 @@
449456 $this->preloadModuleInfo( array_keys( $modules ), $context );
450457 } catch( Exception $e ) {
451458 // Add exception to the output as a comment
452 - $exceptions .= "/*\n{$e->__toString()}\n*/\n";
 459+ $errors .= $this->makeComment( $e->__toString() );
453460 }
454461
455462 wfProfileIn( __METHOD__.'-getModifiedTime' );
456463
457 - $private = false;
458464 // To send Last-Modified and support If-Modified-Since, we need to detect
459465 // the last modified time
460466 $mtime = wfTimestamp( TS_UNIX, $wgCacheEpoch );
@@ -462,22 +468,18 @@
463469 * @var $module ResourceLoaderModule
464470 */
465471 try {
466 - // Bypass Squid and other shared caches if the request includes any private modules
467 - if ( $module->getGroup() === 'private' ) {
468 - $private = true;
469 - }
470472 // Calculate maximum modified time
471473 $mtime = max( $mtime, $module->getModifiedTime( $context ) );
472474 } catch ( Exception $e ) {
473475 // Add exception to the output as a comment
474 - $exceptions .= "/*\n{$e->__toString()}\n*/\n";
 476+ $errors .= $this->makeComment( $e->__toString() );
475477 }
476478 }
477479
478480 wfProfileOut( __METHOD__.'-getModifiedTime' );
479481
480482 // Send content type and cache related headers
481 - $this->sendResponseHeaders( $context, $mtime, $private );
 483+ $this->sendResponseHeaders( $context, $mtime );
482484
483485 // If there's an If-Modified-Since header, respond with a 304 appropriately
484486 if ( $this->tryRespondLastModified( $context, $mtime ) ) {
@@ -489,20 +491,20 @@
490492 $response = $this->makeModuleResponse( $context, $modules, $missing );
491493
492494 // Prepend comments indicating exceptions
493 - $response = $exceptions . $response;
 495+ $response = $errors . $response;
494496
495497 // Capture any PHP warnings from the output buffer and append them to the
496498 // response in a comment if we're in debug mode.
497499 if ( $context->getDebug() && strlen( $warnings = ob_get_contents() ) ) {
498 - $response = "/*\n$warnings\n*/\n" . $response;
 500+ $response = $this->makeComment( $warnings ) . $response;
499501 }
500502
501503 // Remove the output buffer and output the response
502504 ob_end_clean();
503505 echo $response;
504506
505 - // Save response to file cache unless there are private modules or errors
506 - if ( isset( $fileCache ) && !$private && !$exceptions && !$missing ) {
 507+ // Save response to file cache unless there are errors
 508+ if ( isset( $fileCache ) && !$errors && !$missing ) {
507509 // Cache single modules...and other requests if there are enough hits
508510 if ( ResourceFileCache::useFileCache( $context ) ) {
509511 if ( $fileCache->isCacheWorthy() ) {
@@ -520,10 +522,9 @@
521523 * Send content type and last modified headers to the client.
522524 * @param $context ResourceLoaderContext
523525 * @param $mtime string TS_MW timestamp to use for last-modified
524 - * @param $private bool True iff response contains any private modules
525526 * @return void
526527 */
527 - protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime, $private ) {
 528+ protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime ) {
528529 global $wgResourceLoaderMaxage;
529530 // If a version wasn't specified we need a shorter expiry time for updates
530531 // to propagate to clients quickly
@@ -547,13 +548,8 @@
548549 header( 'Cache-Control: private, no-cache, must-revalidate' );
549550 header( 'Pragma: no-cache' );
550551 } else {
551 - if ( $private ) {
552 - header( "Cache-Control: private, max-age=$maxage" );
553 - $exp = $maxage;
554 - } else {
555 - header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" );
556 - $exp = min( $maxage, $smaxage );
557 - }
 552+ header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" );
 553+ $exp = min( $maxage, $smaxage );
558554 header( 'Expires: ' . wfTimestamp( TS_RFC2822, $exp + time() ) );
559555 }
560556 }
@@ -650,6 +646,11 @@
651647 return false; // cache miss
652648 }
653649
 650+ protected function makeComment( $text ) {
 651+ $encText = str_replace( '*/', '* /', $text );
 652+ return "/*\n$encText\n*/\n";
 653+ }
 654+
654655 /**
655656 * Generates code for a response
656657 *
@@ -674,7 +675,7 @@
675676 $blobs = MessageBlobStore::get( $this, $modules, $context->getLanguage() );
676677 } catch ( Exception $e ) {
677678 // Add exception to the output as a comment
678 - $exceptions .= "/*\n{$e->__toString()}\n*/\n";
 679+ $exceptions .= $this->makeComment( $e->__toString() );
679680 }
680681 } else {
681682 $blobs = array();
@@ -753,7 +754,7 @@
754755 }
755756 } catch ( Exception $e ) {
756757 // Add exception to the output as a comment
757 - $exceptions .= "/*\n{$e->__toString()}\n*/\n";
 758+ $exceptions .= $this->makeComment( $e->__toString() );
758759
759760 // Register module as missing
760761 $missing[] = $name;
Index: branches/wmf/1.19wmf1/includes/resourceloader/ResourceLoaderUserOptionsModule.php
@@ -42,41 +42,19 @@
4343 if ( isset( $this->modifiedTime[$hash] ) ) {
4444 return $this->modifiedTime[$hash];
4545 }
46 -
 46+
4747 global $wgUser;
48 -
49 - if ( $context->getUser() === $wgUser->getName() ) {
50 - return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
51 - } else {
52 - return 1;
53 - }
 48+ return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() );
5449 }
5550
5651 /**
57 - * Fetch the context's user options, or if it doesn't match current user,
58 - * the default options.
59 - *
60 - * @param $context ResourceLoaderContext: Context object
61 - * @return Array: List of user options keyed by option name
62 - */
63 - protected function contextUserOptions( ResourceLoaderContext $context ) {
64 - global $wgUser;
65 -
66 - // Verify identity -- this is a private module
67 - if ( $context->getUser() === $wgUser->getName() ) {
68 - return $wgUser->getOptions();
69 - } else {
70 - return User::getDefaultOptions();
71 - }
72 - }
73 -
74 - /**
7552 * @param $context ResourceLoaderContext
7653 * @return string
7754 */
7855 public function getScript( ResourceLoaderContext $context ) {
 56+ global $wgUser;
7957 return Xml::encodeJsCall( 'mw.user.options.set',
80 - array( $this->contextUserOptions( $context ) ) );
 58+ array( $wgUser->getOptions() ) );
8159 }
8260
8361 /**
Index: branches/wmf/1.19wmf1/includes/AutoLoader.php
@@ -49,6 +49,7 @@
5050 'ConfEditorToken' => 'includes/ConfEditor.php',
5151 'Cookie' => 'includes/Cookie.php',
5252 'CookieJar' => 'includes/Cookie.php',
 53+ 'MWCryptRand' => 'includes/CryptRand.php',
5354 'CurlHttpRequest' => 'includes/HttpFunctions.php',
5455 'DeferrableUpdate' => 'includes/DeferredUpdates.php',
5556 'DeferredUpdates' => 'includes/DeferredUpdates.php',
Property changes on: branches/wmf/1.19wmf1/includes/AutoLoader.php
___________________________________________________________________
Modified: svn:mergeinfo
5657 Merged /branches/REL1_19/phase3/includes/AutoLoader.php:r114241,114283,114355
Index: branches/wmf/1.19wmf1/includes/DefaultSettings.php
@@ -2581,13 +2581,6 @@
25822582 );
25832583
25842584 /**
2585 - * Whether to embed private modules inline with HTML output or to bypass
2586 - * caching and check the user parameter against $wgUser to prevent
2587 - * unauthorized access to private modules.
2588 - */
2589 -$wgResourceLoaderInlinePrivateModules = true;
2590 -
2591 -/**
25922585 * The default debug mode (on/off) for of ResourceLoader requests. This will still
25932586 * be overridden when the debug URL parameter is used.
25942587 */
Index: branches/wmf/1.19wmf1/includes/specials/SpecialUserlogin.php
@@ -1136,9 +1136,9 @@
11371137 */
11381138 public static function setLoginToken() {
11391139 global $wgRequest;
1140 - // Use User::generateToken() instead of $user->editToken()
 1140+ // Generate a token directly instead of using $user->editToken()
11411141 // because the latter reuses $_SESSION['wsEditToken']
1142 - $wgRequest->setSessionData( 'wsLoginToken', User::generateToken() );
 1142+ $wgRequest->setSessionData( 'wsLoginToken', MWCryptRand::generateHex( 32 ) );
11431143 }
11441144
11451145 /**
@@ -1162,7 +1162,7 @@
11631163 */
11641164 public static function setCreateaccountToken() {
11651165 global $wgRequest;
1166 - $wgRequest->setSessionData( 'wsCreateaccountToken', User::generateToken() );
 1166+ $wgRequest->setSessionData( 'wsCreateaccountToken', MWCryptRand::generateHex( 32 ) );
11671167 }
11681168
11691169 /**
Index: branches/wmf/1.19wmf1/includes/specials/SpecialUpload.php
@@ -111,14 +111,7 @@
112112
113113 // If it was posted check for the token (no remote POST'ing with user credentials)
114114 $token = $request->getVal( 'wpEditToken' );
115 - if( $this->mSourceType == 'file' && $token == null ) {
116 - // Skip token check for file uploads as that can't be faked via JS...
117 - // Some client-side tools don't expect to need to send wpEditToken
118 - // with their submissions, as that's new in 1.16.
119 - $this->mTokenOk = true;
120 - } else {
121 - $this->mTokenOk = $this->getUser()->matchEditToken( $token );
122 - }
 115+ $this->mTokenOk = $this->getUser()->matchEditToken( $token );
123116
124117 $this->uploadFormTextTop = '';
125118 $this->uploadFormTextAfterSummary = '';
Index: branches/wmf/1.19wmf1/includes/specials/SpecialWatchlist.php
@@ -43,7 +43,7 @@
4444 // Add feed links
4545 $wlToken = $user->getOption( 'watchlisttoken' );
4646 if ( !$wlToken ) {
47 - $wlToken = sha1( mt_rand() . microtime( true ) );
 47+ $wlToken = MWCryptRand::generateHex( 40 );
4848 $user->setOption( 'watchlisttoken', $wlToken );
4949 $user->saveSettings();
5050 }
Property changes on: branches/wmf/1.19wmf1/includes/specials/SpecialWatchlist.php
___________________________________________________________________
Modified: svn:mergeinfo
5151 Merged /branches/REL1_19/phase3/includes/specials/SpecialWatchlist.php:r114241,114283,114355
Property changes on: branches/wmf/1.19wmf1/includes/specials
___________________________________________________________________
Modified: svn:mergeinfo
5252 Merged /branches/REL1_19/phase3/includes/specials:r114241,114283,114355
Property changes on: branches/wmf/1.19wmf1/includes
___________________________________________________________________
Modified: svn:mergeinfo
5353 Merged /branches/REL1_19/phase3/includes:r114241,114283,114355
Property changes on: branches/wmf/1.19wmf1
___________________________________________________________________
Modified: svn:mergeinfo
5454 Merged /branches/REL1_19/phase3:r114241,114283,114355

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r56793Workaround for bugs with Commonist (mwapi-based) and other upload bots....brion23:44, 22 September 2009

Status & tagging log