diff --git a/give.php b/give.php index 78d0cdfc9a..70ba172554 100644 --- a/give.php +++ b/give.php @@ -202,7 +202,7 @@ private function setup_constants() { // Plugin version if ( ! defined( 'GIVE_VERSION' ) ) { - define( 'GIVE_VERSION', '1.3.2' ); + define( 'GIVE_VERSION', '1.3.3' ); } // Plugin Folder Path diff --git a/includes/libraries/class-wp-session.php b/includes/libraries/class-wp-session.php index f0d115c7fe..8244f03537 100644 --- a/includes/libraries/class-wp-session.php +++ b/includes/libraries/class-wp-session.php @@ -5,9 +5,9 @@ * Standardizes WordPress session data using database-backed options for storage. * for storing user session information. * - * @package WordPress + * @package WordPress * @subpackage Session - * @since 3.7.0 + * @since 3.7.0 */ /** @@ -66,17 +66,20 @@ public static function get_instance() { * create a new session with that ID. * * @param $session_id + * * @uses apply_filters Calls `wp_session_expiration` to determine how long until sessions expire. */ protected function __construct() { - if ( isset( $_COOKIE[WP_SESSION_COOKIE] ) ) { - $cookie = stripslashes( $_COOKIE[WP_SESSION_COOKIE] ); + if ( isset( $_COOKIE[ WP_SESSION_COOKIE ] ) ) { + $cookie = stripslashes( $_COOKIE[ WP_SESSION_COOKIE ] ); $cookie_crumbs = explode( '||', $cookie ); - - $this->session_id = $cookie_crumbs[0]; - $this->expires = $cookie_crumbs[1]; + if ( $this->is_valid_md5( $cookie_crumbs[0] ) ) { + $this->session_id = $cookie_crumbs[0]; + } else { + $this->regenerate_id( true ); + } + $this->expires = $cookie_crumbs[1]; $this->exp_variant = $cookie_crumbs[2]; - // Update the session expiration if we're past the variant time if ( time() > $this->exp_variant ) { $this->set_expiration(); @@ -87,9 +90,7 @@ protected function __construct() { $this->session_id = $this->generate_id(); $this->set_expiration(); } - $this->read_data(); - $this->set_cookie(); } @@ -114,14 +115,14 @@ protected function __construct() { */ protected function set_expiration() { $this->exp_variant = time() + (int) apply_filters( 'wp_session_expiration_variant', 24 * 60 ); - $this->expires = time() + (int) apply_filters( 'wp_session_expiration', 30 * 60 ); + $this->expires = time() + (int) apply_filters( 'wp_session_expiration', 30 * 60 ); } /** * Set the session cookie */ protected function set_cookie() { - @setcookie( WP_SESSION_COOKIE, $this->session_id . '||' . $this->expires . '||' . $this->exp_variant , $this->expires, COOKIEPATH, COOKIE_DOMAIN ); + @setcookie( WP_SESSION_COOKIE, $this->session_id . '||' . $this->expires . '||' . $this->exp_variant, $this->expires, COOKIEPATH, COOKIE_DOMAIN ); } /** @@ -130,7 +131,7 @@ protected function set_cookie() { * @return string */ protected function generate_id() { - require_once( ABSPATH . 'wp-includes/class-phpass.php'); + require_once( ABSPATH . 'wp-includes/class-phpass.php' ); $hasher = new PasswordHash( 8, false ); return md5( $hasher->get_random_bytes( 32 ) ); @@ -149,6 +150,17 @@ protected function read_data() { return $this->container; } + /** + * Checks if is valid md5 string + * + * @param string $md5 + * + * @return int + */ + protected function is_valid_md5( $md5 = '' ) { + return preg_match( '/^[a-f0-9]{32}$/', $md5 ); + } + /** * Write the data from the current session to the data storage system. */ @@ -188,6 +200,7 @@ public function json_in( $data ) { if ( is_array( $array ) ) { $this->container = $array; + return true; } @@ -215,7 +228,7 @@ public function regenerate_id( $delete_old = false ) { * @return bool */ public function session_started() { - return !!self::$instance; + return ! ! self::$instance; } /** diff --git a/includes/libraries/wp-session.php b/includes/libraries/wp-session.php index bc87874662..c93b6225eb 100644 --- a/includes/libraries/wp-session.php +++ b/includes/libraries/wp-session.php @@ -5,9 +5,9 @@ * Standardizes WordPress session data and uses either database transients or in-memory caching * for storing user session information. * - * @package WordPress + * @package WordPress * @subpackage Session - * @since 3.7.0 + * @since 3.7.0 */ /** @@ -78,6 +78,7 @@ function wp_session_start() { return $wp_session->session_started(); } + add_action( 'plugins_loaded', 'wp_session_start' ); /** @@ -113,6 +114,7 @@ function wp_session_write_close() { $wp_session->write_data(); do_action( 'wp_session_commit' ); } + add_action( 'shutdown', 'wp_session_write_close' ); /** @@ -132,21 +134,19 @@ function wp_session_cleanup() { if ( ! defined( 'WP_INSTALLING' ) ) { $expiration_keys = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE option_name LIKE '_wp_session_expires_%'" ); - $now = current_time( 'timestamp' ); + $now = current_time( 'timestamp' ); $expired_sessions = array(); - foreach( $expiration_keys as $expiration ) { + foreach ( $expiration_keys as $expiration ) { // If the session has expired if ( $now > intval( $expiration->option_value ) ) { // Get the session ID by parsing the option_name $session_id = substr( $expiration->option_name, 20 ); - - if( (int) -1 === (int) $session_id ) { + if ( (int) -1 === (int) $session_id || ! preg_match( '/^[a-f0-9]{32}$/', $session_id ) ) { continue; } - $expired_sessions[] = $expiration->option_name; - $expired_sessions[] = "_wp_session_$session_id"; + $expired_sessions[] = esc_sql( "_wp_session_$session_id" ); } } @@ -160,6 +160,7 @@ function wp_session_cleanup() { // Allow other plugins to hook in to the garbage collection process. do_action( 'wp_session_cleanup' ); } + add_action( 'wp_session_garbage_collection', 'wp_session_cleanup' ); /** @@ -170,4 +171,5 @@ function wp_session_register_garbage_collection() { wp_schedule_event( current_time( 'timestamp' ), 'twicedaily', 'wp_session_garbage_collection' ); } } + add_action( 'wp', 'wp_session_register_garbage_collection' ); diff --git a/readme.txt b/readme.txt index 4228a56b3a..a4f479dbc8 100644 --- a/readme.txt +++ b/readme.txt @@ -129,6 +129,9 @@ We also really like WooCommerce. It's hands-down the most robust eCommerce platf == Changelog == += 1.3.3 = +* Fixed security vulnerability due to WP session IDs + = 1.3.2 = * New: German/Deutsch translation for Give provided by Konrad - Thanks! :) - https://github.com/WordImpress/Give/issues/383 * New: Polish/Polski translation provided by Emilia - Thanks! :) - https://github.com/WordImpress/Give/issues/418