From 6bac38232ce2ab9ca846b2b696daa14b76471753 Mon Sep 17 00:00:00 2001 From: Andrew Ozz Date: Sat, 2 Aug 2014 20:08:52 +0000 Subject: [PATCH] Add blog_id to the `wp-settings-*` cookie (used for storing user state) to prevent it being overloaded on sub-domain sites. Fixes #29095. git-svn-id: https://develop.svn.wordpress.org/trunk@29362 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/js/utils.js | 138 +++++++++++++++++++----------- src/wp-includes/option.php | 71 +++++++++------ src/wp-includes/script-loader.php | 2 +- 3 files changed, 134 insertions(+), 77 deletions(-) diff --git a/src/wp-includes/js/utils.js b/src/wp-includes/js/utils.js index 4b30bb0ac6..29acaead81 100644 --- a/src/wp-includes/js/utils.js +++ b/src/wp-includes/js/utils.js @@ -3,25 +3,27 @@ // utility functions var wpCookies = { -// The following functions are from Cookie.js class in TinyMCE, Moxiecode, used under LGPL. +// The following functions are from Cookie.js class in TinyMCE 3, Moxiecode, used under LGPL. - each : function(obj, cb, scope) { + each: function( obj, cb, scope ) { var n, l; - if ( !obj ) + if ( ! obj ) { return 0; + } scope = scope || obj; - if ( typeof(obj.length) != 'undefined' ) { + if ( typeof( obj.length ) !== 'undefined' ) { for ( n = 0, l = obj.length; n < l; n++ ) { - if ( cb.call(scope, obj[n], n, obj) === false ) + if ( cb.call( scope, obj[n], n, obj ) === false ) { return 0; + } } } else { for ( n in obj ) { if ( obj.hasOwnProperty(n) ) { - if ( cb.call(scope, obj[n], n, obj) === false ) { + if ( cb.call( scope, obj[n], n, obj ) === false ) { return 0; } } @@ -34,17 +36,18 @@ var wpCookies = { * Get a multi-values cookie. * Returns a JS object with the name: 'value' pairs. */ - getHash : function(name) { - var all = this.get(name), ret; + getHash: function( name ) { + var cookie = this.get( name ), values; - if ( all ) { - this.each( all.split('&'), function(pair) { + if ( cookie ) { + this.each( cookie.split('&'), function( pair ) { pair = pair.split('='); - ret = ret || {}; - ret[pair[0]] = pair[1]; + values = values || {}; + values[pair[0]] = pair[1]; }); } - return ret; + + return values; }, /** @@ -52,45 +55,47 @@ var wpCookies = { * * 'values_obj' is the JS object that is stored. It is encoded as URI in wpCookies.set(). */ - setHash : function(name, values_obj, expires, path, domain, secure) { + setHash: function( name, values_obj, expires, path, domain, secure ) { var str = ''; - this.each(values_obj, function(val, key) { - str += (!str ? '' : '&') + key + '=' + val; + this.each( values_obj, function( val, key ) { + str += ( ! str ? '' : '&' ) + key + '=' + val; }); - this.set(name, str, expires, path, domain, secure); + this.set( name, str, expires, path, domain, secure ); }, /** * Get a cookie. */ - get : function(name) { + get: function( name ) { var e, b, cookie = document.cookie, p = name + '='; - if ( !cookie ) + if ( ! cookie ) { return; + } - b = cookie.indexOf('; ' + p); + b = cookie.indexOf( '; ' + p ); - if ( b == -1 ) { + if ( b === -1 ) { b = cookie.indexOf(p); - if ( b !== 0 ) + if ( b !== 0 ) { return null; - + } } else { b += 2; } e = cookie.indexOf( ';', b ); - if ( e == -1 ) + if ( e === -1 ) { e = cookie.length; + } - return decodeURIComponent( cookie.substring(b + p.length, e) ); + return decodeURIComponent( cookie.substring( b + p.length, e ) ); }, /** @@ -99,13 +104,13 @@ var wpCookies = { * The 'expires' arg can be either a JS Date() object set to the expiration date (back-compat) * or the number of seconds until expiration */ - set : function(name, value, expires, path, domain, secure) { + set: function( name, value, expires, path, domain, secure ) { var d = new Date(); - if ( typeof(expires) == 'object' && expires.toGMTString ) { + if ( typeof( expires ) === 'object' && expires.toGMTString ) { expires = expires.toGMTString(); - } else if ( parseInt(expires, 10) ) { - d.setTime( d.getTime() + ( parseInt(expires, 10) * 1000 ) ); // time must be in miliseconds + } else if ( parseInt( expires, 10 ) ) { + d.setTime( d.getTime() + ( parseInt( expires, 10 ) * 1000 ) ); // time must be in miliseconds expires = d.toGMTString(); } else { expires = ''; @@ -123,41 +128,68 @@ var wpCookies = { * * This is done by setting it to an empty value and setting the expiration time in the past. */ - remove : function(name, path) { - this.set(name, '', -1000, path); + remove: function( name, path ) { + this.set( name, '', -1000, path ); } }; // Returns the value as string. Second arg or empty string is returned when value is not set. function getUserSetting( name, def ) { - var obj = getAllUserSettings(); + var settings = getAllUserSettings(); - if ( obj.hasOwnProperty(name) ) - return obj[name]; + if ( settings.hasOwnProperty( name ) ) { + return settings[name]; + } - if ( typeof def != 'undefined' ) + if ( typeof def !== 'undefined' ) { return def; + } return ''; } // Both name and value must be only ASCII letters, numbers or underscore // and the shorter, the better (cookies can store maximum 4KB). Not suitable to store text. +// The value is converted and stored as string. function setUserSetting( name, value, _del ) { - if ( 'object' !== typeof userSettings ) + if ( 'object' !== typeof userSettings ) { return false; - - var cookie = 'wp-settings-' + userSettings.uid, all = wpCookies.getHash(cookie) || {}, path = userSettings.url, - n = name.toString().replace(/[^A-Za-z0-9_]/, ''), v = value.toString().replace(/[^A-Za-z0-9_]/, ''); - - if ( _del ) { - delete all[n]; - } else { - all[n] = v; } - wpCookies.setHash(cookie, all, 31536000, path); - wpCookies.set('wp-settings-time-'+userSettings.uid, userSettings.time, 31536000, path); + var uid = userSettings.uid, + oldUid = uid.lastIndexOf('-') > 0 ? uid.substring( 0, uid.lastIndexOf('-') ) : 0, + settings = wpCookies.getHash( 'wp-settings-' + uid ), + path = userSettings.url; + + name = name.toString().replace( /[^A-Za-z0-9_]/, '' ); + + if ( typeof value === 'number' ) { + value = parseInt( value, 10 ); + } else { + value = value.toString().replace( /[^A-Za-z0-9_]/, '' ); + } + + if ( oldUid ) { + if ( ! settings ) { + settings = wpCookies.getHash( 'wp-settings-' + oldUid ); + } + // Delete old cookies + if ( wpCookies.get( 'wp-settings-time-' + oldUid ) ) { + wpCookies.remove( 'wp-settings-' + oldUid, path ); + wpCookies.remove( 'wp-settings-time-' + oldUid, path ); + } + } + + settings = settings || {}; + + if ( _del ) { + delete settings[name]; + } else { + settings[name] = value; + } + + wpCookies.setHash( 'wp-settings-' + uid, settings, 31536000, path ); + wpCookies.set( 'wp-settings-time-' + uid, userSettings.time, 31536000, path ); return name; } @@ -168,8 +200,18 @@ function deleteUserSetting( name ) { // Returns all settings as js object. function getAllUserSettings() { - if ( 'object' !== typeof userSettings ) + if ( 'object' !== typeof userSettings ) { return {}; + } - return wpCookies.getHash('wp-settings-' + userSettings.uid) || {}; + var uid = userSettings.uid, + settings = wpCookies.getHash( 'wp-settings-' + uid ); + + // Try the old format cookie + if ( ! settings && uid.lastIndexOf('-') > 0 ) { + uid = uid.substring( 0, uid.lastIndexOf('-') ); + settings = wpCookies.getHash( 'wp-settings-' + uid ); + } + + return settings || {}; } diff --git a/src/wp-includes/option.php b/src/wp-includes/option.php index 77c95fdc1f..bbbfef83bd 100644 --- a/src/wp-includes/option.php +++ b/src/wp-includes/option.php @@ -713,29 +713,30 @@ function set_transient( $transient, $value, $expiration = 0 ) { */ function wp_user_settings() { - if ( ! is_admin() ) + if ( ! is_admin() || defined( 'DOING_AJAX' ) ) { return; + } - if ( defined('DOING_AJAX') ) + if ( ! $user_id = get_current_user_id() ) { return; + } - if ( ! $user_id = get_current_user_id() ) - return; - - if ( is_super_admin() && ! is_user_member_of_blog() ) + if ( is_super_admin() && ! is_user_member_of_blog() ) { return; + } $settings = (string) get_user_option( 'user-settings', $user_id ); + $uid = $user_id . '-' . get_current_blog_id(); - if ( isset( $_COOKIE['wp-settings-' . $user_id] ) ) { - $cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE['wp-settings-' . $user_id] ); + if ( isset( $_COOKIE['wp-settings-' . $uid] ) ) { + $cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE['wp-settings-' . $uid] ); // No change or both empty if ( $cookie == $settings ) return; $last_saved = (int) get_user_option( 'user-settings-time', $user_id ); - $current = isset( $_COOKIE['wp-settings-time-' . $user_id]) ? preg_replace( '/[^0-9]/', '', $_COOKIE['wp-settings-time-' . $user_id] ) : 0; + $current = isset( $_COOKIE['wp-settings-time-' . $uid]) ? preg_replace( '/[^0-9]/', '', $_COOKIE['wp-settings-time-' . $uid] ) : 0; // The cookie is newer than the saved value. Update the user_option and leave the cookie as-is if ( $current > $last_saved ) { @@ -747,9 +748,9 @@ function wp_user_settings() { // The cookie is not set in the current browser or the saved value is newer. $secure = ( 'https' === parse_url( site_url(), PHP_URL_SCHEME ) ); - setcookie( 'wp-settings-' . $user_id, $settings, time() + YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN, $secure ); - setcookie( 'wp-settings-time-' . $user_id, time(), time() + YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN, $secure ); - $_COOKIE['wp-settings-' . $user_id] = $settings; + setcookie( 'wp-settings-' . $uid, $settings, time() + YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN, $secure ); + setcookie( 'wp-settings-time-' . $uid, time(), time() + YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN, $secure ); + $_COOKIE['wp-settings-' . $uid] = $settings; } /** @@ -781,8 +782,9 @@ function get_user_setting( $name, $default = false ) { */ function set_user_setting( $name, $value ) { - if ( headers_sent() ) + if ( headers_sent() ) { return false; + } $all_user_settings = get_all_user_settings(); $all_user_settings[$name] = $value; @@ -803,8 +805,9 @@ function set_user_setting( $name, $value ) { */ function delete_user_setting( $names ) { - if ( headers_sent() ) + if ( headers_sent() ) { return false; + } $all_user_settings = get_all_user_settings(); $names = (array) $names; @@ -817,8 +820,9 @@ function delete_user_setting( $names ) { } } - if ( $deleted ) + if ( $deleted ) { return wp_set_all_user_settings( $all_user_settings ); + } return false; } @@ -833,22 +837,28 @@ function delete_user_setting( $names ) { function get_all_user_settings() { global $_updated_user_settings; - if ( ! $user_id = get_current_user_id() ) + if ( ! $user_id = get_current_user_id() ) { return array(); + } - if ( isset( $_updated_user_settings ) && is_array( $_updated_user_settings ) ) + if ( isset( $_updated_user_settings ) && is_array( $_updated_user_settings ) ) { return $_updated_user_settings; + } $user_settings = array(); - if ( isset( $_COOKIE['wp-settings-' . $user_id] ) ) { + $uid = $user_id . '-' . get_current_blog_id(); + + if ( isset( $_COOKIE['wp-settings-' . $uid] ) ) { + $cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE['wp-settings-' . $uid] ); + } elseif ( isset( $_COOKIE['wp-settings-' . $user_id] ) ) { $cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE['wp-settings-' . $user_id] ); + } - if ( $cookie && strpos( $cookie, '=' ) ) // '=' cannot be 1st char - parse_str( $cookie, $user_settings ); - + if ( ! empty( $cookie ) && strpos( $cookie, '=' ) ) { // '=' cannot be 1st char + parse_str( $cookie, $user_settings ); } else { $option = get_user_option( 'user-settings', $user_id ); - if ( $option && is_string($option) ) + if ( $option && is_string( $option ) ) parse_str( $option, $user_settings ); } @@ -867,22 +877,25 @@ function get_all_user_settings() { function wp_set_all_user_settings( $user_settings ) { global $_updated_user_settings; - if ( ! $user_id = get_current_user_id() ) + if ( ! $user_id = get_current_user_id() ) { return false; + } - if ( is_super_admin() && ! is_user_member_of_blog() ) + if ( is_super_admin() && ! is_user_member_of_blog() ) { return; + } $settings = ''; foreach ( $user_settings as $name => $value ) { $_name = preg_replace( '/[^A-Za-z0-9_]+/', '', $name ); $_value = preg_replace( '/[^A-Za-z0-9_]+/', '', $value ); - if ( ! empty( $_name ) ) + if ( ! empty( $_name ) ) { $settings .= $_name . '=' . $_value . '&'; + } } - $settings = rtrim($settings, '&'); + $settings = rtrim( $settings, '&' ); parse_str( $settings, $_updated_user_settings ); update_user_option( $user_id, 'user-settings', $settings, false ); @@ -897,11 +910,13 @@ function wp_set_all_user_settings( $user_settings ) { * @since 2.7.0 */ function delete_all_user_settings() { - if ( ! $user_id = get_current_user_id() ) + if ( ! $user_id = get_current_user_id() ) { return; + } + $uid = $user_id . '-' . get_current_blog_id(); update_user_option( $user_id, 'user-settings', '', false ); - setcookie('wp-settings-' . $user_id, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH); + setcookie( 'wp-settings-' . $uid, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH ); } /** diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 2e7a5d852b..a74e0614c6 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -72,7 +72,7 @@ function wp_default_scripts( &$scripts ) { $scripts->add( 'utils', "/wp-includes/js/utils$suffix.js" ); did_action( 'init' ) && $scripts->localize( 'utils', 'userSettings', array( 'url' => (string) SITECOOKIEPATH, - 'uid' => (string) get_current_user_id(), + 'uid' => get_current_user_id() . '-' . get_current_blog_id(), 'time' => (string) time(), ) );