WordPress.org

Make WordPress Core

Changeset 29221


Ignore:
Timestamp:
07/18/2014 09:12:05 AM (5 years ago)
Author:
nacin
Message:

Tie cookies and nonces to user sessions so they may be invalidated upon logout.

Sessions are stored in usermeta via WP_User_Meta_Session_Tokens, which extends the abstract WP_Session_Tokens class. Extending WP_Session_Tokens can allow for alternative storage, such as a separate table or Redis.

Introduces some simple APIs for session listing and destruction, such as wp_get_active_sessions() and wp_destroy_all_sessions().

This invalidates all existing authentication cookies, as a new segment (the session token) has been added to them.

props duck_, nacin, mdawaffe.
see #20276.

Location:
trunk
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/pluggable.php

    r28939 r29221  
    587587 */
    588588function wp_logout() {
     589    wp_destroy_current_session();
    589590    wp_clear_auth_cookie();
    590591
     
    632633    $username = $cookie_elements['username'];
    633634    $hmac = $cookie_elements['hmac'];
     635    $token = $cookie_elements['token'];
    634636    $expired = $expiration = $cookie_elements['expiration'];
    635637
     
    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 
    672     if ( hash_hmac( 'md5', $hmac, $key ) !== hash_hmac( 'md5', $hash, $key ) ) {
     671    $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
     672    $hash = hash_hmac( 'sha256', $username . '|' . $expiration . '|' . $token, $key );
     673
     674    if ( hash_hmac( 'sha256', $hmac, $key ) !== hash_hmac( 'sha256', $hash, $key ) ) {
    673675        /**
    674676         * Fires if a bad authentication cookie hash is encountered.
     
    682684    }
    683685
    684     if ( $expiration < time() ) {// AJAX/POST grace period set above
     686    $manager = WP_Session_Tokens::get_instance( $user->ID );
     687    if ( ! $manager->verify_token( $token ) ) {
     688        do_action( 'auth_cookie_bad_session_token', $cookie_elements );
     689        return false;
     690    }
     691
     692    // AJAX/POST grace period set above
     693    if ( $expiration < time() ) {
    685694        $GLOBALS['login_grace_period'] = 1;
    686695    }
     
    709718 * @param int $expiration Cookie expiration in seconds
    710719 * @param string $scheme Optional. The cookie scheme to use: auth, secure_auth, or logged_in
    711  * @return string Authentication cookie contents
    712  */
    713 function wp_generate_auth_cookie($user_id, $expiration, $scheme = 'auth') {
     720 * @param string $token User's session token to use for this cookie
     721 * @return string Authentication cookie contents. Empty string if user does not exist.
     722 */
     723function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
    714724    $user = get_userdata($user_id);
     725    if ( ! $user ) {
     726        return '';
     727    }
     728
     729    if ( ! $token ) {
     730        $manager = WP_Session_Tokens::get_instance( $user_id );
     731        $token = $manager->create_token( $expiration );
     732    }
    715733
    716734    $pass_frag = substr($user->user_pass, 8, 4);
    717735
    718     $key = wp_hash($user->user_login . $pass_frag . '|' . $expiration, $scheme);
    719     $hash = hash_hmac('md5', $user->user_login . '|' . $expiration, $key);
    720 
    721     $cookie = $user->user_login . '|' . $expiration . '|' . $hash;
     736    $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
     737    $hash = hash_hmac( 'sha256', $user->user_login . '|' . $expiration . '|' . $token, $key );
     738
     739    $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
    722740
    723741    /**
     
    730748     * @param int    $expiration Authentication cookie expiration in seconds.
    731749     * @param string $scheme     Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'.
    732      */
    733     return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme );
     750     * @param string $token      User's session token used.
     751     */
     752    return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme, $token );
    734753}
    735754endif;
     
    773792
    774793    $cookie_elements = explode('|', $cookie);
    775     if ( count($cookie_elements) != 3 )
     794    if ( count( $cookie_elements ) !== 4 ) {
    776795        return false;
    777 
    778     list($username, $expiration, $hmac) = $cookie_elements;
    779 
    780     return compact('username', 'expiration', 'hmac', 'scheme');
     796    }
     797
     798    list( $username, $expiration, $token, $hmac ) = $cookie_elements;
     799
     800    return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
    781801}
    782802endif;
     
    794814 * @param int $user_id User ID
    795815 * @param bool $remember Whether to remember the user
     816 * @param mixed $secure  Whether the admin cookies should only be sent over HTTPS.
     817 *                       Default is_ssl().
    796818 */
    797819function wp_set_auth_cookie($user_id, $remember = false, $secure = '') {
     
    855877    }
    856878
    857     $auth_cookie = wp_generate_auth_cookie($user_id, $expiration, $scheme);
    858     $logged_in_cookie = wp_generate_auth_cookie($user_id, $expiration, 'logged_in');
     879    $manager = WP_Session_Tokens::get_instance( $user_id );
     880    $token = $manager->create_token( $expiration );
     881
     882    $auth_cookie = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token );
     883    $logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token );
    859884
    860885    /**
     
    16831708    }
    16841709
     1710    $token = wp_get_session_token();
    16851711    $i = wp_nonce_tick();
    16861712
    16871713    // Nonce generated 0-12 hours ago
    1688     if ( substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10) === $nonce )
     1714    if ( $nonce === substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 ) ) {
    16891715        return 1;
     1716    }
     1717
    16901718    // Nonce generated 12-24 hours ago
    1691     if ( substr(wp_hash(($i - 1) . $action . $uid, 'nonce'), -12, 10) === $nonce )
     1719    if ( $nonce === substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ) ) {
    16921720        return 2;
     1721    }
     1722
    16931723    // Invalid nonce
    16941724    return false;
     
    17131743    }
    17141744
     1745    $token = wp_get_session_token();
    17151746    $i = wp_nonce_tick();
    17161747
    1717     return substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10);
     1748    return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    17181749}
    17191750endif;
  • trunk/src/wp-includes/user.php

    r29165 r29221  
    21742174    return $user_id;
    21752175}
     2176
     2177/**
     2178 * Retrieve the current session token from the logged_in cookie.
     2179 *
     2180 * @since 4.0.0
     2181 *
     2182 * @return string Token.
     2183 */
     2184function wp_get_session_token() {
     2185    $cookie = wp_parse_auth_cookie( '', 'logged_in' );
     2186    return ! empty( $cookie['token'] ) ? $cookie['token'] : '';
     2187}
     2188
     2189/**
     2190 * Retrieve a list of sessions for the current user.
     2191 *
     2192 * @since 4.0.0
     2193 * @return array Array of sessions.
     2194 */
     2195function wp_get_all_sessions() {
     2196    $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
     2197    return $manager->get_all_sessions();
     2198}
     2199
     2200/**
     2201 * Remove the current session token from the database.
     2202 *
     2203 * @since 4.0.0
     2204 */
     2205function wp_destroy_current_session() {
     2206    $token = wp_get_session_token();
     2207    if ( $token ) {
     2208        $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
     2209        $manager->destroy_token( $token );
     2210    }
     2211}
     2212
     2213/**
     2214 * Remove all but the current session token for the current user for the database.
     2215 *
     2216 * @since 4.0.0
     2217 */
     2218function wp_destroy_other_sessions() {
     2219    $token = wp_get_session_token();
     2220    if ( $token ) {
     2221        $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
     2222        $manager->destroy_other_tokens( $token );
     2223    }
     2224}
     2225
     2226/**
     2227 * Remove all session tokens for the current user from the database.
     2228 *
     2229 * @since 4.0.0
     2230 */
     2231function wp_destroy_all_sessions() {
     2232    $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
     2233    $manager->destroy_all_tokens();
     2234}
  • trunk/src/wp-settings.php

    r29044 r29221  
    120120require( ABSPATH . WPINC . '/template.php' );
    121121require( ABSPATH . WPINC . '/user.php' );
     122require( ABSPATH . WPINC . '/session.php' );
    122123require( ABSPATH . WPINC . '/meta.php' );
    123124require( ABSPATH . WPINC . '/general-template.php' );
Note: See TracChangeset for help on using the changeset viewer.