WordPress.org

Make WordPress Core

Ticket #20276: 20276.4.diff

File 20276.4.diff, 7.9 KB (added by nacin, 5 years ago)

Refreshed. Would like to make it easy to replace session storage, such as spinning it off to a separate table for sites with already crazy usermeta tables like .com or .org. Either well-placed filters or could be ripe for an interface/object.

  • src/wp-includes/pluggable.php

     
    586586 * @since 2.5.0
    587587 */
    588588function wp_logout() {
     589        wp_destroy_current_session();
    589590        wp_clear_auth_cookie();
    590591
    591592        /**
     
    631632        $scheme = $cookie_elements['scheme'];
    632633        $username = $cookie_elements['username'];
    633634        $hmac = $cookie_elements['hmac'];
     635        $token = $cookie_elements['token'];
    634636        $expired = $expiration = $cookie_elements['expiration'];
    635637
    636638        // Allow a grace period for POST and AJAX requests
     
    666668
    667669        $pass_frag = substr($user->user_pass, 8, 4);
    668670
    669         $key = wp_hash($username . $pass_frag . '|' . $expiration, $scheme);
    670         $hash = hash_hmac('md5', $username . '|' . $expiration, $key);
     671        $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
     672        $hash = hash_hmac( 'md5', $username . '|' . $expiration . '|' . $token, $key );
    671673
    672674        if ( hash_hmac( 'md5', $hmac, $key ) !== hash_hmac( 'md5', $hash, $key ) ) {
    673675                /**
     
    681683                return false;
    682684        }
    683685
    684         if ( $expiration < time() ) {// AJAX/POST grace period set above
     686        if ( ! wp_verify_session_token( $user->ID, $token ) ) {
     687                do_action( 'auth_cookie_bad_session_token', $cookie_elements );
     688                return false;
     689        }
     690
     691        if ( $expiration < time() ) { // AJAX/POST grace period set above
    685692                $GLOBALS['login_grace_period'] = 1;
    686693        }
    687694
     
    708715 * @param int $user_id User ID
    709716 * @param int $expiration Cookie expiration in seconds
    710717 * @param string $scheme Optional. The cookie scheme to use: auth, secure_auth, or logged_in
    711  * @return string Authentication cookie contents
     718 * @param string $token User's session token to use for this cookie
     719 * @return string Authentication cookie contents. Empty string if user does not exist.
    712720 */
    713 function wp_generate_auth_cookie($user_id, $expiration, $scheme = 'auth') {
     721function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
    714722        $user = get_userdata($user_id);
     723        if ( ! $user ) {
     724                return '';
     725        }
    715726
     727        if ( ! $token ) {
     728                $token = wp_create_session_token( $user_id, $expiration );
     729        }
     730
    716731        $pass_frag = substr($user->user_pass, 8, 4);
    717732
    718         $key = wp_hash($user->user_login . $pass_frag . '|' . $expiration, $scheme);
    719         $hash = hash_hmac('md5', $user->user_login . '|' . $expiration, $key);
     733        $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
     734        $hash = hash_hmac( 'md5', $user->user_login . '|' . $expiration . '|' . $token, $key );
    720735
    721         $cookie = $user->user_login . '|' . $expiration . '|' . $hash;
     736        $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
    722737
    723738        /**
    724739         * Filter the authentication cookie.
     
    772787        }
    773788
    774789        $cookie_elements = explode('|', $cookie);
    775         if ( count($cookie_elements) != 3 )
     790        if ( count( $cookie_elements ) !== 4 ) {
    776791                return false;
     792        }
    777793
    778         list($username, $expiration, $hmac) = $cookie_elements;
     794        list( $username, $expiration, $token, $hmac ) = $cookie_elements;
    779795
    780         return compact('username', 'expiration', 'hmac', 'scheme');
     796        return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
    781797}
    782798endif;
    783799
     
    793809 *
    794810 * @param int $user_id User ID
    795811 * @param bool $remember Whether to remember the user
     812 * @param mixed $secure  Whether the admin cookies should only be sent over HTTPS.
     813 *                       Default is_ssl().
    796814 */
    797815function wp_set_auth_cookie($user_id, $remember = false, $secure = '') {
    798816        if ( $remember ) {
     
    850868                $scheme = 'auth';
    851869        }
    852870
    853         $auth_cookie = wp_generate_auth_cookie($user_id, $expiration, $scheme);
    854         $logged_in_cookie = wp_generate_auth_cookie($user_id, $expiration, 'logged_in');
     871        $token = wp_create_session_token( $user_id, $expiration );
     872        $auth_cookie = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token );
     873        $logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token );
    855874
    856875        /**
    857876         * Fires immediately before the authentication cookie is set.
     
    16781697                $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
    16791698        }
    16801699
     1700        $token = wp_get_session_token();
    16811701        $i = wp_nonce_tick();
    16821702
    16831703        // Nonce generated 0-12 hours ago
    1684         if ( substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10) === $nonce )
     1704        if ( $nonce === substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 ) ) {
    16851705                return 1;
     1706        }
     1707
    16861708        // Nonce generated 12-24 hours ago
    1687         if ( substr(wp_hash(($i - 1) . $action . $uid, 'nonce'), -12, 10) === $nonce )
     1709        if ( $nonce === substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ) ) {
    16881710                return 2;
     1711        }
     1712
    16891713        // Invalid nonce
    16901714        return false;
    16911715}
     
    17081732                $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
    17091733        }
    17101734
     1735        $token = wp_get_session_token();
    17111736        $i = wp_nonce_tick();
    17121737
    1713         return substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10);
     1738        return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    17141739}
    17151740endif;
    17161741
  • src/wp-includes/user.php

     
    21272127
    21282128        return $user_id;
    21292129}
     2130
     2131/**
     2132 * Generate a cookie session identification token.
     2133 *
     2134 * A session identification token is a long, random string. It is used to
     2135 * link a cookie to an expiration time and to ensure that cookies become
     2136 * invalidated upon logout. This function generates a token and stores it
     2137 * as user meta with the associated expiration time.
     2138 *
     2139 * Will also remove any expired tokens from the database.
     2140 *
     2141 * @since 4.0.0
     2142 *
     2143 * @param int $user_id User to create a token for.
     2144 * @param int $expiration Session expiration timestamp.
     2145 * @return string Session identification token.
     2146 */
     2147function wp_create_session_token( $user_id, $expiration ) {
     2148        $sessions = get_user_meta( $user_id, 'session_tokens', true );
     2149        if ( ! $sessions )
     2150                $sessions = array();
     2151        // Expire old sessions
     2152        $time = time();
     2153        foreach ( $sessions as $v => $e ) {
     2154                if ( $e < $time ) {
     2155                        unset( $sessions[ $v ] );
     2156                }
     2157        }
     2158
     2159        $token = wp_generate_password( 40, false, false );
     2160        $verifier = hash( 'sha256', $token );
     2161        $sessions[ $verifier ] = $expiration;
     2162
     2163        update_user_meta( $user_id, 'session_tokens', $sessions );
     2164
     2165        return $token;
     2166}
     2167
     2168/**
     2169 * Validate a user's session token as authentic.
     2170 *
     2171 * Checks that the given token is present in the database and hasn't expired.
     2172 *
     2173 * @since 4.0.0
     2174 *
     2175 * @param int $user_id User to verify against.
     2176 * @param string $token Token to verify.
     2177 * @return bool Whether the token is valid for the given user.
     2178 */
     2179function wp_verify_session_token( $user_id, $token ) {
     2180        $sessions = get_user_meta( $user_id, 'session_tokens', true );
     2181        $verifier = hash( 'sha256', $token );
     2182        return isset( $sessions[ $verifier ] ) && $sessions[ $verifier ] >= time();
     2183}
     2184
     2185/**
     2186 * Remove the current session token from the database.
     2187 *
     2188 * Will also remove any expired tokens from the database.
     2189 *
     2190 * @since 4.0.0
     2191 */
     2192function wp_destroy_current_session() {
     2193        $token = wp_get_session_token();
     2194        if ( ! empty( $token ) ) {
     2195                $user_id = get_current_user_id();
     2196                $verifier = hash( 'sha256', $token );
     2197                $sessions = get_user_meta( $user_id, 'session_tokens', true );
     2198                unset( $sessions[ $verifier ] );
     2199
     2200                // Expire old sessions
     2201                $time = time();
     2202                foreach ( $sessions as $v => $e ) {
     2203                        if ( $e < $time ) {
     2204                                unset( $sessions[ $v ] );
     2205                        }
     2206                }
     2207
     2208                if ( $sessions ) {
     2209                        update_user_meta( $user_id, 'session_tokens', $sessions );
     2210                } else {
     2211                        delete_user_meta( $user_id, 'session_tokens' );
     2212                }
     2213        }
     2214}
     2215
     2216/**
     2217 * Retrieve the current session token from the logged_in cookie.
     2218 *
     2219 * @since 4.0.0
     2220 *
     2221 * @return string
     2222 */
     2223function wp_get_session_token() {
     2224        $cookie = wp_parse_auth_cookie( '', 'logged_in' );
     2225        return ! empty( $cookie['token'] ) ? $cookie['token'] : '';
     2226}