upskill-event-manager/includes/class-hvac-secure-storage.php
bengizmo 5ab2c58f68 feat: Implement comprehensive security fixes for production deployment
- Fix production debug exposure in Zoho admin interface (WP_DEBUG conditional)
- Implement secure credential storage with AES-256-CBC encryption
- Add file upload size limits (5MB profiles, 2MB logos) with enhanced validation
- Fix privilege escalation via PHP Reflection bypass with public method alternative
- Add comprehensive input validation and security headers
- Update plugin version to 1.0.7 with security hardening

Security improvements:
 Debug information exposure eliminated in production
 API credentials now encrypted in database storage
 File upload security enhanced with size/type validation
 AJAX endpoints secured with proper capability checks
 SQL injection protection verified via parameterized queries
 CSRF protection maintained with nonce verification

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-06 13:31:38 -03:00

186 lines
No EOL
4.8 KiB
PHP

<?php
/**
* HVAC Secure Storage - Encrypted credential storage
*
* @package HVAC_Community_Events
* @since 1.0.7
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* HVAC_Secure_Storage class
*
* Provides encrypted storage for sensitive data like API keys
*/
class HVAC_Secure_Storage {
/**
* Encryption method
*/
const ENCRYPTION_METHOD = 'AES-256-CBC';
/**
* Get encryption key
*
* @return string
*/
private static function get_encryption_key() {
$key = get_option('hvac_encryption_key');
if (!$key) {
// Generate a new key if one doesn't exist
$key = base64_encode(random_bytes(32));
update_option('hvac_encryption_key', $key);
}
return base64_decode($key);
}
/**
* Encrypt data
*
* @param string $data Data to encrypt
* @return string Encrypted data
*/
public static function encrypt($data) {
if (empty($data)) {
return '';
}
$key = self::get_encryption_key();
$iv = random_bytes(openssl_cipher_iv_length(self::ENCRYPTION_METHOD));
$encrypted = openssl_encrypt($data, self::ENCRYPTION_METHOD, $key, 0, $iv);
if ($encrypted === false) {
return false;
}
return base64_encode($iv . $encrypted);
}
/**
* Decrypt data
*
* @param string $encrypted_data Encrypted data
* @return string|false Decrypted data or false on failure
*/
public static function decrypt($encrypted_data) {
if (empty($encrypted_data)) {
return '';
}
$data = base64_decode($encrypted_data);
if ($data === false) {
return false;
}
$key = self::get_encryption_key();
$iv_length = openssl_cipher_iv_length(self::ENCRYPTION_METHOD);
if (strlen($data) < $iv_length) {
return false;
}
$iv = substr($data, 0, $iv_length);
$encrypted = substr($data, $iv_length);
return openssl_decrypt($encrypted, self::ENCRYPTION_METHOD, $key, 0, $iv);
}
/**
* Store encrypted credential
*
* @param string $option_name Option name
* @param string $value Value to store
* @return bool Success
*/
public static function store_credential($option_name, $value) {
if (empty($value)) {
delete_option($option_name);
return true;
}
$encrypted = self::encrypt($value);
if ($encrypted === false) {
return false;
}
return update_option($option_name, $encrypted);
}
/**
* Retrieve encrypted credential
*
* @param string $option_name Option name
* @param string $default Default value
* @return string Decrypted value
*/
public static function get_credential($option_name, $default = '') {
$encrypted = get_option($option_name, '');
if (empty($encrypted)) {
return $default;
}
$decrypted = self::decrypt($encrypted);
return $decrypted !== false ? $decrypted : $default;
}
/**
* Migrate existing plaintext credentials to encrypted storage
*
* @return array Migration results
*/
public static function migrate_existing_credentials() {
$credentials_to_migrate = [
'hvac_zoho_client_id',
'hvac_zoho_client_secret',
'hvac_zoho_refresh_token',
'hvac_google_maps_api_key'
];
$results = [
'migrated' => 0,
'skipped' => 0,
'errors' => []
];
foreach ($credentials_to_migrate as $option_name) {
$plaintext = get_option($option_name, '');
if (empty($plaintext)) {
$results['skipped']++;
continue;
}
// Check if it's already encrypted (basic heuristic)
$decrypted = self::decrypt($plaintext);
if ($decrypted !== false && $decrypted !== $plaintext) {
$results['skipped']++;
continue;
}
// Migrate to encrypted storage
if (self::store_credential($option_name, $plaintext)) {
$results['migrated']++;
} else {
$results['errors'][] = "Failed to migrate: $option_name";
}
}
return $results;
}
/**
* Check if OpenSSL is available
*
* @return bool
*/
public static function is_encryption_available() {
return function_exists('openssl_encrypt') && function_exists('openssl_decrypt');
}
}