HIGH 级别修复: - AdminPage.php: 移除重复的 Logger 导入 - AdminPage.php: 修复 auth_token 双重加密问题 - AdminPage.php: 添加 action 参数白名单验证 - CacheManager.php: get_stats() 使用 $wpdb->prepare() - source-editor.php: 不显示加密后的 token,使用占位符 - Validator.php: DNS 解析失败时视为本地地址(SSRF 防护) MEDIUM 级别修复: - BridgeCommand.php: 使用 WordPress 文件系统 API - SourceModel.php: 改进 auth_token 解密回退逻辑 - SourceModel.php: 添加 URL 协议验证 (http/https) - Encryption.php: 使用随机生成的密钥替代站点 URL 哈希 LOW 级别修复: - BackgroundUpdater.php: 移除 PHP 8.0 联合类型语法 - Plugin.php: 使用 wp_cache_flush_group() 如果可用 - Validator.php: 添加 IPv6 私有地址检查 - uninstall.php: 清理 wpbridge_encryption_key 选项 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
212 lines
5.1 KiB
PHP
212 lines
5.1 KiB
PHP
<?php
|
||
/**
|
||
* 密钥加密
|
||
*
|
||
* @package WPBridge
|
||
*/
|
||
|
||
namespace WPBridge\Security;
|
||
|
||
// 防止直接访问
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* 密钥加密类
|
||
*/
|
||
class Encryption {
|
||
|
||
/**
|
||
* 加密方法
|
||
*
|
||
* @var string
|
||
*/
|
||
const METHOD = 'aes-256-cbc';
|
||
|
||
/**
|
||
* 获取加密密钥
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function get_key(): string {
|
||
// 优先使用自定义密钥
|
||
if ( defined( 'WPBRIDGE_ENCRYPTION_KEY' ) && WPBRIDGE_ENCRYPTION_KEY ) {
|
||
return WPBRIDGE_ENCRYPTION_KEY;
|
||
}
|
||
|
||
// 使用 WordPress 的 AUTH_KEY
|
||
if ( defined( 'AUTH_KEY' ) && AUTH_KEY ) {
|
||
return AUTH_KEY;
|
||
}
|
||
|
||
// 最后使用 SECURE_AUTH_KEY
|
||
if ( defined( 'SECURE_AUTH_KEY' ) && SECURE_AUTH_KEY ) {
|
||
return SECURE_AUTH_KEY;
|
||
}
|
||
|
||
// 如果都没有,生成并存储一个随机密钥
|
||
$key = get_option( 'wpbridge_encryption_key' );
|
||
if ( empty( $key ) ) {
|
||
$key = bin2hex( random_bytes( 32 ) );
|
||
update_option( 'wpbridge_encryption_key', $key, false );
|
||
}
|
||
return $key;
|
||
}
|
||
|
||
/**
|
||
* 加密数据
|
||
*
|
||
* @param string $data 明文数据
|
||
* @return string 加密后的数据(base64 编码)
|
||
*/
|
||
public static function encrypt( string $data ): string {
|
||
if ( empty( $data ) ) {
|
||
return '';
|
||
}
|
||
|
||
$key = hash( 'sha256', self::get_key(), true );
|
||
$iv = openssl_random_pseudo_bytes( openssl_cipher_iv_length( self::METHOD ) );
|
||
|
||
$encrypted = openssl_encrypt( $data, self::METHOD, $key, OPENSSL_RAW_DATA, $iv );
|
||
|
||
if ( false === $encrypted ) {
|
||
return '';
|
||
}
|
||
|
||
// 将 IV 和加密数据一起存储
|
||
return base64_encode( $iv . $encrypted );
|
||
}
|
||
|
||
/**
|
||
* 解密数据
|
||
*
|
||
* @param string $data 加密数据(base64 编码)
|
||
* @return string 解密后的明文
|
||
*/
|
||
public static function decrypt( string $data ): string {
|
||
if ( empty( $data ) ) {
|
||
return '';
|
||
}
|
||
|
||
$data = base64_decode( $data );
|
||
|
||
if ( false === $data ) {
|
||
return '';
|
||
}
|
||
|
||
$key = hash( 'sha256', self::get_key(), true );
|
||
$iv_length = openssl_cipher_iv_length( self::METHOD );
|
||
|
||
if ( strlen( $data ) < $iv_length ) {
|
||
return '';
|
||
}
|
||
|
||
$iv = substr( $data, 0, $iv_length );
|
||
$encrypted = substr( $data, $iv_length );
|
||
|
||
$decrypted = openssl_decrypt( $encrypted, self::METHOD, $key, OPENSSL_RAW_DATA, $iv );
|
||
|
||
if ( false === $decrypted ) {
|
||
return '';
|
||
}
|
||
|
||
return $decrypted;
|
||
}
|
||
|
||
/**
|
||
* 检查数据是否已加密
|
||
*
|
||
* @param string $data 数据
|
||
* @return bool
|
||
*/
|
||
public static function is_encrypted( string $data ): bool {
|
||
if ( empty( $data ) ) {
|
||
return false;
|
||
}
|
||
|
||
// 检查是否是有效的 base64
|
||
$decoded = base64_decode( $data, true );
|
||
|
||
if ( false === $decoded ) {
|
||
return false;
|
||
}
|
||
|
||
// 检查长度是否足够包含 IV
|
||
$iv_length = openssl_cipher_iv_length( self::METHOD );
|
||
|
||
return strlen( $decoded ) > $iv_length;
|
||
}
|
||
|
||
/**
|
||
* 安全地存储敏感数据
|
||
*
|
||
* @param string $key 选项键
|
||
* @param string $value 敏感值
|
||
* @return bool
|
||
*/
|
||
public static function store_secure( string $key, string $value ): bool {
|
||
$encrypted = self::encrypt( $value );
|
||
return update_option( 'wpbridge_secure_' . $key, $encrypted );
|
||
}
|
||
|
||
/**
|
||
* 安全地获取敏感数据
|
||
*
|
||
* @param string $key 选项键
|
||
* @param string $default 默认值
|
||
* @return string
|
||
*/
|
||
public static function get_secure( string $key, string $default = '' ): string {
|
||
$encrypted = get_option( 'wpbridge_secure_' . $key, '' );
|
||
|
||
if ( empty( $encrypted ) ) {
|
||
return $default;
|
||
}
|
||
|
||
$decrypted = self::decrypt( $encrypted );
|
||
|
||
return ! empty( $decrypted ) ? $decrypted : $default;
|
||
}
|
||
|
||
/**
|
||
* 删除安全存储的数据
|
||
*
|
||
* @param string $key 选项键
|
||
* @return bool
|
||
*/
|
||
public static function delete_secure( string $key ): bool {
|
||
return delete_option( 'wpbridge_secure_' . $key );
|
||
}
|
||
|
||
/**
|
||
* 生成随机令牌
|
||
*
|
||
* @param int $length 长度
|
||
* @return string
|
||
*/
|
||
public static function generate_token( int $length = 32 ): string {
|
||
return bin2hex( random_bytes( $length / 2 ) );
|
||
}
|
||
|
||
/**
|
||
* 哈希密码/令牌(用于比较)
|
||
*
|
||
* @param string $data 数据
|
||
* @return string
|
||
*/
|
||
public static function hash( string $data ): string {
|
||
return hash( 'sha256', $data . self::get_key() );
|
||
}
|
||
|
||
/**
|
||
* 验证哈希
|
||
*
|
||
* @param string $data 原始数据
|
||
* @param string $hash 哈希值
|
||
* @return bool
|
||
*/
|
||
public static function verify_hash( string $data, string $hash ): bool {
|
||
return hash_equals( self::hash( $data ), $hash );
|
||
}
|
||
}
|