WordPress.org

Make WordPress Core

Ticket #20276: 20276.backpress.diff

File 20276.backpress.diff, 6.0 KB (added by duck_, 6 years ago)
  • includes/class.wp-auth.php

     
    148148         */
    149149        function validate_auth_cookie( $cookie = null, $scheme = 'auth' )
    150150        {
    151                 if ( empty( $cookie ) ) {
    152                         foreach ( $this->cookies[$scheme] as $_scheme_cookie ) {
    153                                 // Take the first cookie of type scheme that exists
    154                                 if ( !empty( $_COOKIE[$_scheme_cookie['name']] ) ) {
    155                                         $cookie = $_COOKIE[$_scheme_cookie['name']];
    156                                         break;
    157                                 }
    158                         }
    159                 }
    160                
    161                 if ( !$cookie ) {
     151                if ( ! $cookie_elements = $this->parse_auth_cookie( $cookie, $scheme ) ) {
     152                        do_action( 'auth_cookie_malformed', $cookie );
    162153                        return false;
    163154                }
     155                extract( $cookie_elements );
    164156
    165                 $cookie_elements = explode( '|', $cookie );
    166                 if ( count( $cookie_elements ) != 3 ) {
    167                         do_action( 'auth_cookie_malformed', $cookie, $scheme );
    168                         return false;
    169                 }
    170 
    171                 list( $username, $expiration, $hmac ) = $cookie_elements;
    172 
    173157                $expired = $expiration;
    174158
    175159                // Allow a grace period for POST and AJAX requests
     
    193177                        $pass_frag = substr( $user->user_pass, 8, 4 );
    194178                }
    195179
    196                 $key  = call_user_func( backpress_get_option( 'hash_function_name' ), $username . $pass_frag . '|' . $expiration, $scheme );
    197                 $hash = hash_hmac( 'md5', $username . '|' . $expiration, $key );
     180                $key  = call_user_func( backpress_get_option( 'hash_function_name' ), $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
     181                $hash = hash_hmac( 'md5', $username . '|' . $expiration . '|' . $token, $key );
    198182       
    199183                if ( $hmac != $hash ) {
    200184                        do_action( 'auth_cookie_bad_hash', $cookie_elements );
     
    201185                        return false;
    202186                }
    203187
     188                if ( ! $this->verify_session_token( $user, $token ) ) {
     189                        do_action( 'auth_cookie_bad_session_token', $cookie_elements );
     190                        return false;
     191                }
     192
    204193                do_action( 'auth_cookie_valid', $cookie_elements, $user );
    205194
    206195                return $user->ID;
     
    215204         *
    216205         * @param int $user_id User ID
    217206         * @param int $expiration Cookie expiration in seconds
     207         * @param string $scheme The cookie scheme to use: auth, secure_auth, or logged_in
     208         * @param string $token User's session token to use for this cookie
    218209         * @return string Authentication cookie contents
    219210         */
    220         function generate_auth_cookie( $user_id, $expiration, $scheme = 'auth' )
     211        function generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' )
    221212        {
    222213                $user = $this->users->get_user( $user_id );
    223214                if ( !$user || is_wp_error( $user ) ) {
     
    224215                        return $user;
    225216                }
    226217
     218                if ( empty( $token ) )
     219                        $token = $this->create_session_token( $user, $expiration );
     220
    227221                $pass_frag = '';
    228222                if ( 1 < WP_AUTH_COOKIE_VERSION ) {
    229223                        $pass_frag = substr( $user->user_pass, 8, 4 );
    230224                }
    231225
    232                 $key  = call_user_func( backpress_get_option( 'hash_function_name' ), $user->user_login . $pass_frag . '|' . $expiration, $scheme );
    233                 $hash = hash_hmac('md5', $user->user_login . '|' . $expiration, $key);
     226                $key  = call_user_func( backpress_get_option( 'hash_function_name' ), $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
     227                $hash = hash_hmac('md5', $user->user_login . '|' . $expiration . '|' . $token, $key);
    234228
    235                 $cookie = $user->user_login . '|' . $expiration . '|' . $hash;
     229                $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
    236230
    237231                return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme );
    238232        }
     
    264258
    265259                $expire = absint( $expire );
    266260
     261                $token = $this->create_session_token( $user_id, $expiration );
     262
    267263                foreach ( $this->cookies[$scheme] as $_cookie ) {
    268                         $cookie = $this->generate_auth_cookie( $user_id, $expiration, $scheme );
     264                        $cookie = $this->generate_auth_cookie( $user_id, $expiration, $scheme, $token );
    269265                        if ( is_wp_error( $cookie ) ) {
    270266                                return $cookie;
    271267                        }
     
    302298                }
    303299                unset( $_scheme, $_scheme_cookies );
    304300        }
     301
     302        function create_session_token( $user, $expiration ) {
     303                if ( ! is_object( $user ) )
     304                        $user = $this->users->get_user( $user );
     305
     306                $sessions = ! empty( $user->session_tokens ) ? $user->session_tokens : array();
     307
     308                $time = time();
     309                foreach ( $sessions as $v => $e ) {
     310                        if ( $e < $time )
     311                                unset( $sessions[ $v ] );
     312                }
     313
     314                $token = WP_Pass::generate_password( 40, false );
     315                $verifier = hash( 'sha256', $token );
     316                $sessions[ $verifier ] = $expiration;
     317
     318                $GLOBALS['wp_users_object']->update_meta( array( 'id' => $user->ID, 'meta_key' => 'session_tokens', 'meta_value' => $sessions ) );
     319
     320                return $token;
     321        }
     322
     323        function verify_session_token( $user, $token ) {
     324                $verifier = hash( 'sha256', $token );
     325                return isset( $user->session_tokens[ $verifier ] ) && $user->session_tokens[ $verifier ] >= time();
     326        }
     327
     328        function destroy_current_session() {
     329                $cookie = $this->parse_auth_cookie( '', 'logged_in' );
     330                if ( ! empty( $cookie['token'] ) ) {
     331                        $user = $this->get_current_user();
     332                        $verifier = hash( 'sha256', $cookie['token'] );
     333                        $sessions = ! empty( $user->session_tokens ) ? $user->session_tokens : array();
     334                        unset( $sessions[ $verifier ] );
     335
     336                        $time = time();
     337                        foreach ( $sessions as $v => $e ) {
     338                                if ( $e < $time )
     339                                        unset( $sessions[ $v ] );
     340                        }
     341
     342                        $GLOBALS['wp_users_object']->update_meta( array( 'id' => $user->ID, 'meta_key' => 'session_tokens', 'meta_value' => $sessions ) );
     343                }
     344        }
     345
     346        function parse_auth_cookie( $cookie = '', $scheme = 'auth' ) {
     347                if ( empty( $cookie ) ) {
     348                        foreach ( $this->cookies[$scheme] as $_scheme_cookie ) {
     349                                // Take the first cookie of type scheme that exists
     350                                if ( !empty( $_COOKIE[$_scheme_cookie['name']] ) ) {
     351                                        $cookie = $_COOKIE[$_scheme_cookie['name']];
     352                                        break;
     353                                }
     354                        }
     355                }
     356               
     357                if ( ! $cookie )
     358                        return false;
     359
     360                $cookie_elements = explode( '|', $cookie );
     361                if ( count( $cookie_elements ) != 4 )
     362                        return false;
     363
     364                list( $username, $expiration, $token, $hmac ) = $cookie_elements;
     365                return compact( 'username', 'expiration', 'token', 'hmac' );
     366        }
    305367}