类文档

WP_Recovery_Mode_Key_Service

💡 云策文档标注

概述

WP_Recovery_Mode_Key_Service 是 WordPress 核心类,用于生成和验证进入恢复模式所需的密钥。它管理密钥的生命周期,包括创建、存储、验证和清理过期密钥。

关键要点

  • 核心功能:生成恢复模式令牌和密钥,验证密钥有效性,并管理密钥存储。
  • 密钥管理:使用选项 'recovery_keys' 存储密钥记录,包含哈希密钥和创建时间戳。
  • 安全性:密钥使用 wp_fast_hash() 进行哈希处理(自 6.8.0 版本起),支持一次性使用和过期检查。
  • 主要方法:包括 generate_recovery_mode_token、generate_and_store_recovery_mode_key、validate_recovery_mode_key、clean_expired_keys 等。
  • 私有方法:get_keys 和 update_keys 用于内部密钥记录操作,remove_key 用于移除已使用密钥。

代码示例

// 示例:生成和验证恢复模式密钥
$service = new WP_Recovery_Mode_Key_Service();
$token = $service->generate_recovery_mode_token();
$key = $service->generate_and_store_recovery_mode_key($token);
$result = $service->validate_recovery_mode_key($token, $key, 3600); // TTL 为 3600 秒

注意事项

  • 密钥验证时会消耗密钥,确保一次性使用。
  • 自 WordPress 6.8.0 起,密钥哈希方式从 phpass 改为 wp_fast_hash(),但旧密钥可能仍使用 phpass。
  • clean_expired_keys 方法需定期调用以清理过期密钥,避免存储膨胀。
  • 类为 final,不可继承。

📄 原文内容

Core class used to generate and validate keys used to enter Recovery Mode.

Methods

Name Description
WP_Recovery_Mode_Key_Service::clean_expired_keys Removes expired recovery mode keys.
WP_Recovery_Mode_Key_Service::generate_and_store_recovery_mode_key Creates a recovery mode key.
WP_Recovery_Mode_Key_Service::generate_recovery_mode_token Creates a recovery mode token.
WP_Recovery_Mode_Key_Service::get_keys Gets the recovery key records.
WP_Recovery_Mode_Key_Service::remove_key Removes a used recovery key.
WP_Recovery_Mode_Key_Service::update_keys Updates the recovery key records.
WP_Recovery_Mode_Key_Service::validate_recovery_mode_key Verifies if the recovery mode key is correct.

Source

final class WP_Recovery_Mode_Key_Service {

	/**
	 * The option name used to store the keys.
	 *
	 * @since 5.2.0
	 * @var string
	 */
	private $option_name = 'recovery_keys';

	/**
	 * Creates a recovery mode token.
	 *
	 * @since 5.2.0
	 *
	 * @return string A random string to identify its associated key in storage.
	 */
	public function generate_recovery_mode_token() {
		return wp_generate_password( 22, false );
	}

	/**
	 * Creates a recovery mode key.
	 *
	 * @since 5.2.0
	 * @since 6.8.0 The stored key is now hashed using wp_fast_hash() instead of phpass.
	 *
	 * @param string $token A token generated by <a href="https://developer.wordpress.org/reference/functions/generate_recovery_mode_token/">generate_recovery_mode_token()</a>.
	 * @return string Recovery mode key.
	 */
	public function generate_and_store_recovery_mode_key( $token ) {
		$key = wp_generate_password( 22, false );

		$records = $this->get_keys();

		$records[ $token ] = array(
			'hashed_key' => wp_fast_hash( $key ),
			'created_at' => time(),
		);

		$this->update_keys( $records );

		/**
		 * Fires when a recovery mode key is generated.
		 *
		 * @since 5.2.0
		 *
		 * @param string $token The recovery data token.
		 * @param string $key   The recovery mode key.
		 */
		do_action( 'generate_recovery_mode_key', $token, $key );

		return $key;
	}

	/**
	 * Verifies if the recovery mode key is correct.
	 *
	 * Recovery mode keys can only be used once; the key will be consumed in the process.
	 *
	 * @since 5.2.0
	 *
	 * @param string $token The token used when generating the given key.
	 * @param string $key   The plain text key.
	 * @param int    $ttl   Time in seconds for the key to be valid for.
	 * @return true|WP_Error True on success, error object on failure.
	 */
	public function validate_recovery_mode_key( $token, $key, $ttl ) {
		$records = $this->get_keys();

		if ( ! isset( $records[ $token ] ) ) {
			return new WP_Error( 'token_not_found', __( 'Recovery Mode not initialized.' ) );
		}

		$record = $records[ $token ];

		$this->remove_key( $token );

		if ( ! is_array( $record ) || ! isset( $record['hashed_key'], $record['created_at'] ) ) {
			return new WP_Error( 'invalid_recovery_key_format', __( 'Invalid recovery key format.' ) );
		}

		if ( ! wp_verify_fast_hash( $key, $record['hashed_key'] ) ) {
			return new WP_Error( 'hash_mismatch', __( 'Invalid recovery key.' ) );
		}

		if ( time() > $record['created_at'] + $ttl ) {
			return new WP_Error( 'key_expired', __( 'Recovery key expired.' ) );
		}

		return true;
	}

	/**
	 * Removes expired recovery mode keys.
	 *
	 * @since 5.2.0
	 *
	 * @param int $ttl Time in seconds for the keys to be valid for.
	 */
	public function clean_expired_keys( $ttl ) {

		$records = $this->get_keys();

		foreach ( $records as $key => $record ) {
			if ( ! isset( $record['created_at'] ) || time() > $record['created_at'] + $ttl ) {
				unset( $records[ $key ] );
			}
		}

		$this->update_keys( $records );
	}

	/**
	 * Removes a used recovery key.
	 *
	 * @since 5.2.0
	 *
	 * @param string $token The token used when generating a recovery mode key.
	 */
	private function remove_key( $token ) {

		$records = $this->get_keys();

		if ( ! isset( $records[ $token ] ) ) {
			return;
		}

		unset( $records[ $token ] );

		$this->update_keys( $records );
	}

	/**
	 * Gets the recovery key records.
	 *
	 * @since 5.2.0
	 * @since 6.8.0 Each key is now hashed using wp_fast_hash() instead of phpass.
	 *              Existing keys may still be hashed using phpass.
	 *
	 * @return array {
	 *     Associative array of token => data pairs, where the data is an associative
	 *     array of information about the key.
	 *
	 *     @type array ...$0 {
	 *         Information about the key.
	 *
	 *         @type string $hashed_key The hashed value of the key.
	 *         @type int    $created_at The timestamp when the key was created.
	 *     }
	 * }
	 */
	private function get_keys() {
		return (array) get_option( $this->option_name, array() );
	}

	/**
	 * Updates the recovery key records.
	 *
	 * @since 5.2.0
	 * @since 6.8.0 Each key should now be hashed using wp_fast_hash() instead of phpass.
	 *
	 * @param array $keys {
	 *     Associative array of token => data pairs, where the data is an associative
	 *     array of information about the key.
	 *
	 *     @type array ...$0 {
	 *         Information about the key.
	 *
	 *         @type string $hashed_key The hashed value of the key.
	 *         @type int    $created_at The timestamp when the key was created.
	 *     }
	 * }
	 * @return bool True on success, false on failure.
	 */
	private function update_keys( array $keys ) {
		return update_option( $this->option_name, $keys, false );
	}
}

Changelog

Version Description
5.2.0 Introduced.