ver 1.4.0
This commit is contained in:
217
includes/class-Security.php
Normal file
217
includes/class-Security.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
class CHECKER_SECURITY {
|
||||
|
||||
/**
|
||||
* Check rate limit for an IP address
|
||||
*
|
||||
* @param int $checker_id Checker post ID
|
||||
* @param string $ip IP address to check
|
||||
* @return array ['allowed' => bool, 'message' => string, 'remaining' => int]
|
||||
*/
|
||||
public static function check_rate_limit($checker_id, $ip) {
|
||||
$checker = get_post_meta($checker_id, 'checker', true);
|
||||
|
||||
// Check if rate limiting is enabled
|
||||
if (!isset($checker['security']['rate_limit']['enabled']) || $checker['security']['rate_limit']['enabled'] !== 'yes') {
|
||||
return ['allowed' => true, 'remaining' => 999];
|
||||
}
|
||||
|
||||
// Get settings
|
||||
$max_attempts = isset($checker['security']['rate_limit']['max_attempts']) ? (int)$checker['security']['rate_limit']['max_attempts'] : 5;
|
||||
$time_window = isset($checker['security']['rate_limit']['time_window']) ? (int)$checker['security']['rate_limit']['time_window'] : 15;
|
||||
$block_duration = isset($checker['security']['rate_limit']['block_duration']) ? (int)$checker['security']['rate_limit']['block_duration'] : 60;
|
||||
$error_message = isset($checker['security']['rate_limit']['error_message']) ? $checker['security']['rate_limit']['error_message'] : 'Too many attempts. Please try again later.';
|
||||
|
||||
// Create transient keys
|
||||
$transient_key = 'checker_rate_' . $checker_id . '_' . md5($ip);
|
||||
$block_key = 'checker_block_' . $checker_id . '_' . md5($ip);
|
||||
|
||||
// Check if IP is blocked
|
||||
$blocked_until = get_transient($block_key);
|
||||
if ($blocked_until !== false) {
|
||||
$remaining_time = ceil(($blocked_until - time()) / 60);
|
||||
return [
|
||||
'allowed' => false,
|
||||
'message' => $error_message . ' (' . $remaining_time . ' minutes remaining)',
|
||||
'remaining' => 0
|
||||
];
|
||||
}
|
||||
|
||||
// Get current attempts
|
||||
$attempts = get_transient($transient_key);
|
||||
if ($attempts === false) {
|
||||
$attempts = 0;
|
||||
}
|
||||
|
||||
// Increment attempts
|
||||
$attempts++;
|
||||
|
||||
// Check if exceeded limit
|
||||
if ($attempts > $max_attempts) {
|
||||
// Block the IP
|
||||
set_transient($block_key, time() + ($block_duration * 60), $block_duration * 60);
|
||||
// Reset attempts counter
|
||||
delete_transient($transient_key);
|
||||
|
||||
return [
|
||||
'allowed' => false,
|
||||
'message' => $error_message,
|
||||
'remaining' => 0
|
||||
];
|
||||
}
|
||||
|
||||
// Update attempts counter
|
||||
set_transient($transient_key, $attempts, $time_window * 60);
|
||||
|
||||
return [
|
||||
'allowed' => true,
|
||||
'remaining' => $max_attempts - $attempts
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify reCAPTCHA v3 token
|
||||
*
|
||||
* @param int $checker_id Checker post ID
|
||||
* @param string $token reCAPTCHA token from frontend
|
||||
* @return array ['success' => bool, 'score' => float, 'message' => string]
|
||||
*/
|
||||
public static function verify_recaptcha($checker_id, $token) {
|
||||
$checker = get_post_meta($checker_id, 'checker', true);
|
||||
|
||||
// Check if reCAPTCHA is enabled
|
||||
if (!isset($checker['security']['recaptcha']['enabled']) || $checker['security']['recaptcha']['enabled'] !== 'yes') {
|
||||
return ['success' => true, 'score' => 1.0];
|
||||
}
|
||||
|
||||
// Get settings
|
||||
$secret_key = isset($checker['security']['recaptcha']['secret_key']) ? $checker['security']['recaptcha']['secret_key'] : '';
|
||||
$min_score = isset($checker['security']['recaptcha']['min_score']) ? (float)$checker['security']['recaptcha']['min_score'] : 0.5;
|
||||
|
||||
if (empty($secret_key) || empty($token)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'score' => 0,
|
||||
'message' => 'reCAPTCHA verification failed: Missing credentials'
|
||||
];
|
||||
}
|
||||
|
||||
// Verify with Google
|
||||
$response = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', [
|
||||
'body' => [
|
||||
'secret' => $secret_key,
|
||||
'response' => $token
|
||||
]
|
||||
]);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'score' => 0,
|
||||
'message' => 'reCAPTCHA verification failed: ' . $response->get_error_message()
|
||||
];
|
||||
}
|
||||
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (!isset($body['success']) || !$body['success']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'score' => 0,
|
||||
'message' => 'reCAPTCHA verification failed'
|
||||
];
|
||||
}
|
||||
|
||||
$score = isset($body['score']) ? (float)$body['score'] : 0;
|
||||
|
||||
if ($score < $min_score) {
|
||||
return [
|
||||
'success' => false,
|
||||
'score' => $score,
|
||||
'message' => 'reCAPTCHA score too low. Please try again.'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'score' => $score
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Cloudflare Turnstile token
|
||||
*
|
||||
* @param int $checker_id Checker post ID
|
||||
* @param string $token Turnstile token from frontend
|
||||
* @return array ['success' => bool, 'message' => string]
|
||||
*/
|
||||
public static function verify_turnstile($checker_id, $token) {
|
||||
$checker = get_post_meta($checker_id, 'checker', true);
|
||||
|
||||
// Check if Turnstile is enabled
|
||||
if (!isset($checker['security']['turnstile']['enabled']) || $checker['security']['turnstile']['enabled'] !== 'yes') {
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
// Get settings
|
||||
$secret_key = isset($checker['security']['turnstile']['secret_key']) ? $checker['security']['turnstile']['secret_key'] : '';
|
||||
|
||||
if (empty($secret_key) || empty($token)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Turnstile verification failed: Missing credentials'
|
||||
];
|
||||
}
|
||||
|
||||
// Verify with Cloudflare
|
||||
$response = wp_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
||||
'body' => [
|
||||
'secret' => $secret_key,
|
||||
'response' => $token
|
||||
]
|
||||
]);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Turnstile verification failed: ' . $response->get_error_message()
|
||||
];
|
||||
}
|
||||
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (!isset($body['success']) || !$body['success']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Turnstile verification failed'
|
||||
];
|
||||
}
|
||||
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client IP address
|
||||
*
|
||||
* @return string IP address
|
||||
*/
|
||||
public static function get_client_ip() {
|
||||
$ip = '';
|
||||
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
} else {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
// If multiple IPs, get the first one
|
||||
if (strpos($ip, ',') !== false) {
|
||||
$ip = trim(explode(',', $ip)[0]);
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user