218 lines
7.5 KiB
PHP
218 lines
7.5 KiB
PHP
<?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;
|
|
}
|
|
}
|