Ticket #20276: 20276.5.diff
File 20276.5.diff, 10.4 KB (added by , 10 years ago) |
---|
-
src/wp-includes/pluggable.php
586 586 * @since 2.5.0 587 587 */ 588 588 function wp_logout() { 589 wp_destroy_current_session(); 589 590 wp_clear_auth_cookie(); 590 591 591 592 /** … … 631 632 $scheme = $cookie_elements['scheme']; 632 633 $username = $cookie_elements['username']; 633 634 $hmac = $cookie_elements['hmac']; 635 $token = $cookie_elements['token']; 634 636 $expired = $expiration = $cookie_elements['expiration']; 635 637 636 638 // Allow a grace period for POST and AJAX requests … … 666 668 667 669 $pass_frag = substr($user->user_pass, 8, 4); 668 670 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 ); 671 673 672 674 if ( hash_hmac( 'md5', $hmac, $key ) !== hash_hmac( 'md5', $hash, $key ) ) { 673 675 /** … … 681 683 return false; 682 684 } 683 685 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 if ( $expiration < time() ) { // AJAX/POST grace period set above 685 693 $GLOBALS['login_grace_period'] = 1; 686 694 } 687 695 … … 708 716 * @param int $user_id User ID 709 717 * @param int $expiration Cookie expiration in seconds 710 718 * @param string $scheme Optional. The cookie scheme to use: auth, secure_auth, or logged_in 711 * @return string Authentication cookie contents 719 * @param string $token User's session token to use for this cookie 720 * @return string Authentication cookie contents. Empty string if user does not exist. 712 721 */ 713 function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth') {722 function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) { 714 723 $user = get_userdata($user_id); 724 if ( ! $user ) { 725 return ''; 726 } 715 727 728 if ( ! $token ) { 729 $manager = WP_Session_Tokens::get_instance( $user_id ); 730 $token = $manager->create_token( $expiration ); 731 } 732 716 733 $pass_frag = substr($user->user_pass, 8, 4); 717 734 718 $key = wp_hash( $user->user_login . $pass_frag . '|' . $expiration, $scheme);719 $hash = hash_hmac( 'md5', $user->user_login . '|' . $expiration, $key);735 $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme ); 736 $hash = hash_hmac( 'md5', $user->user_login . '|' . $expiration . '|' . $token, $key ); 720 737 721 $cookie = $user->user_login . '|' . $expiration . '|' . $ hash;738 $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash; 722 739 723 740 /** 724 741 * Filter the authentication cookie. … … 772 789 } 773 790 774 791 $cookie_elements = explode('|', $cookie); 775 if ( count( $cookie_elements) != 3 )792 if ( count( $cookie_elements ) !== 4 ) { 776 793 return false; 794 } 777 795 778 list( $username, $expiration, $hmac) = $cookie_elements;796 list( $username, $expiration, $token, $hmac ) = $cookie_elements; 779 797 780 return compact( 'username', 'expiration', 'hmac', 'scheme');798 return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' ); 781 799 } 782 800 endif; 783 801 … … 793 811 * 794 812 * @param int $user_id User ID 795 813 * @param bool $remember Whether to remember the user 814 * @param mixed $secure Whether the admin cookies should only be sent over HTTPS. 815 * Default is_ssl(). 796 816 */ 797 817 function wp_set_auth_cookie($user_id, $remember = false, $secure = '') { 798 818 if ( $remember ) { … … 850 870 $scheme = 'auth'; 851 871 } 852 872 853 $ auth_cookie = wp_generate_auth_cookie($user_id, $expiration, $scheme);854 $ logged_in_cookie = wp_generate_auth_cookie($user_id, $expiration, 'logged_in');873 $manager = WP_Session_Tokens::get_instance( $user_id ); 874 $token = $manager->create_token( $expiration ); 855 875 876 $auth_cookie = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token ); 877 $logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token ); 878 856 879 /** 857 880 * Fires immediately before the authentication cookie is set. 858 881 * … … 1678 1701 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); 1679 1702 } 1680 1703 1704 $token = wp_get_session_token(); 1681 1705 $i = wp_nonce_tick(); 1682 1706 1683 1707 // Nonce generated 0-12 hours ago 1684 if ( substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10) === $nonce )1708 if ( $nonce === substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 ) ) { 1685 1709 return 1; 1710 } 1711 1686 1712 // Nonce generated 12-24 hours ago 1687 if ( substr(wp_hash(($i - 1) . $action . $uid, 'nonce'), -12, 10) === $nonce )1713 if ( $nonce === substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ) ) { 1688 1714 return 2; 1715 } 1716 1689 1717 // Invalid nonce 1690 1718 return false; 1691 1719 } … … 1708 1736 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); 1709 1737 } 1710 1738 1739 $token = wp_get_session_token(); 1711 1740 $i = wp_nonce_tick(); 1712 1741 1713 return substr( wp_hash($i . $action . $uid, 'nonce'), -12, 10);1742 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); 1714 1743 } 1715 1744 endif; 1716 1745 -
src/wp-includes/user.php
2127 2127 2128 2128 return $user_id; 2129 2129 } 2130 2131 /** 2132 * Class for managing a user's session tokens. 2133 */ 2134 class WP_Session_Tokens { 2135 2136 /** 2137 * User ID. 2138 * 2139 * @since 4.0.0 2140 * 2141 * @var int User ID. 2142 */ 2143 protected $user_id; 2144 2145 /** 2146 * Protected constructor. 2147 * 2148 * @param int $user_id User whose session to manage. 2149 */ 2150 protected function __construct( $user_id ) { 2151 $this->user_id = $user_id; 2152 } 2153 2154 /** 2155 * Get a session token manager instance for a user. 2156 * 2157 * This method contains a filter that allows a plugin to swap out 2158 * the session manager for a subclass of WP_Session_Tokens. 2159 * 2160 * @since 4.0.0 2161 * 2162 * @param int $user_id User whose session to manage. 2163 */ 2164 final public static function get_instance( $user_id ) { 2165 /** 2166 * Filter the session token manager used. 2167 * 2168 * @since 4.0.0 2169 * 2170 * @param string $session Name of class to use as the manager. 2171 * Default 'WP_Session_Tokens'. 2172 */ 2173 $manager = apply_filters( 'session_token_manager', 'WP_Session_Tokens' ); 2174 return new $manager( $user_id ); 2175 } 2176 2177 /** 2178 * Validate a user's session token as authentic. 2179 * 2180 * Checks that the given token is present in the database and hasn't expired. 2181 * 2182 * @since 4.0.0 2183 * 2184 * @param string $token Token to verify. 2185 * @return bool Whether the token is valid for the user. 2186 */ 2187 final public function verify_token( $token ) { 2188 $sessions = $this->get_sessions(); 2189 $verifier = hash( 'sha256', $token ); 2190 return isset( $sessions[ $verifier ] ) && $sessions[ $verifier ]['expiration'] >= time(); 2191 } 2192 2193 /** 2194 * Generate a cookie session identification token. 2195 * 2196 * A session identification token is a long, random string. It is used to 2197 * link a cookie to an expiration time and to ensure that cookies become 2198 * invalidated upon logout. This function generates a token and stores it 2199 * as user meta with the associated expiration time. 2200 * 2201 * Will also remove any expired tokens from the database. 2202 * 2203 * @since 4.0.0 2204 * 2205 * @param int $expiration Session expiration timestamp. 2206 * @return string Session identification token. 2207 */ 2208 final public function create_token( $expiration ) { 2209 /** 2210 * Filter the information attached to the newly created session. 2211 * 2212 * Could be used in the future to attach information such as 2213 * IP address or user agent to a session. 2214 * 2215 * @since 4.0.0 2216 * 2217 * @param array $session Array of extra data. 2218 * @param int $user_id User ID. 2219 */ 2220 $session = apply_filters( 'attach_session_information', array(), $this->user_id ); 2221 $session['expiration'] = $expiration; 2222 2223 $sessions = $this->get_sessions(); 2224 if ( ! $sessions ) 2225 $sessions = array(); 2226 2227 $token = wp_generate_password( 40, false, false ); 2228 $verifier = hash( 'sha256', $token ); 2229 2230 $sessions[ $verifier ] = $session; 2231 2232 $this->update_sessions( $sessions ); 2233 return $token; 2234 } 2235 2236 /** 2237 * Remove a session token from the database. 2238 * 2239 * Will also remove any expired tokens from the database. 2240 * 2241 * @since 4.0.0 2242 */ 2243 final public function destroy_session( $token ) { 2244 $sessions = $this->get_sessions(); 2245 $verifier = hash( 'sha256', $token ); 2246 unset( $sessions[ $verifier ] ); 2247 2248 $this->update_sessions( $sessions ); 2249 } 2250 2251 /** 2252 * Remove all session tokens from the database. 2253 * 2254 * @since 4.0.0 2255 */ 2256 final public function destroy_sessions() { 2257 $this->update_sessions(); 2258 } 2259 2260 /** 2261 * Get all sessions of a user. 2262 * 2263 * @since 4.0.0 2264 * 2265 * @return array 2266 */ 2267 public function get_sessions() { 2268 $sessions = get_user_meta( $this->user_id, 'session_tokens', true ); 2269 2270 if ( ! is_array( $sessions ) ) { 2271 return array(); 2272 } 2273 2274 foreach ( $sessions as &$session ) { 2275 if ( is_int( $session ) ) { 2276 $session = array( 'expiration' => $session ); 2277 } 2278 } 2279 return $sessions; 2280 } 2281 2282 /** 2283 * Update a user's sessions. 2284 * 2285 * @since 4.0.0 2286 * 2287 * @param array $sessions 2288 */ 2289 protected function update_sessions( $sessions ) { 2290 // Remove expired sessions. 2291 $time = time(); 2292 $reduce = ! has_filter( 'attach_session_information' ); 2293 foreach ( $sessions as $verifier => $session ) { 2294 if ( $session['expiration'] < $time ) { 2295 unset( $sessions[ $verifier ] ); 2296 } 2297 2298 if ( $reduce ) { 2299 $sessions[ $verifier ] = $session['expiration']; 2300 } 2301 } 2302 2303 if ( $sessions ) { 2304 update_user_meta( $this->user_id, 'session_tokens', $sessions ); 2305 } else { 2306 delete_user_meta( $this->user_id, 'session_tokens' ); 2307 } 2308 } 2309 } 2310 2311 /** 2312 * Remove the current session token from the database. 2313 * 2314 * Will also remove any expired tokens from the database. 2315 * 2316 * @since 4.0.0 2317 */ 2318 function wp_destroy_current_session() { 2319 $token = wp_get_session_token(); 2320 if ( $token ) { 2321 $manager = WP_Session_Tokens::get_instance( get_current_user_id() ); 2322 $manager->destroy_session( $token ); 2323 } 2324 } 2325 2326 /** 2327 * Retrieve the current session token from the logged_in cookie. 2328 * 2329 * @since 4.0.0 2330 * 2331 * @return string 2332 */ 2333 function wp_get_session_token() { 2334 $cookie = wp_parse_auth_cookie( '', 'logged_in' ); 2335 return ! empty( $cookie['token'] ) ? $cookie['token'] : ''; 2336 }