initial commit
This commit is contained in:
parent
d066133bd4
commit
e6f71e3706
55 changed files with 11928 additions and 0 deletions
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
/**
|
||||
* Rate Limiter for Maple Local Fonts.
|
||||
*
|
||||
* @package Maple_Local_Fonts
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class MLF_Rate_Limiter
|
||||
*
|
||||
* Provides rate limiting functionality to prevent abuse of AJAX endpoints.
|
||||
*/
|
||||
class MLF_Rate_Limiter {
|
||||
|
||||
/**
|
||||
* Default rate limit (requests per window).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $limit = 10;
|
||||
|
||||
/**
|
||||
* Default time window in seconds.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $window = 60;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $limit Maximum requests allowed per window.
|
||||
* @param int $window Time window in seconds.
|
||||
*/
|
||||
public function __construct($limit = 10, $window = 60) {
|
||||
$this->limit = absint($limit);
|
||||
$this->window = absint($window);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user/IP is rate limited.
|
||||
*
|
||||
* @param string $action The action being rate limited.
|
||||
* @return bool True if rate limited (should block), false if allowed.
|
||||
*/
|
||||
public function is_limited($action) {
|
||||
$key = $this->get_rate_limit_key($action);
|
||||
$data = get_transient($key);
|
||||
|
||||
if ($data === false) {
|
||||
// First request, not limited
|
||||
return false;
|
||||
}
|
||||
|
||||
return $data['count'] >= $this->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a request for rate limiting purposes.
|
||||
*
|
||||
* @param string $action The action being recorded.
|
||||
* @return void
|
||||
*/
|
||||
public function record_request($action) {
|
||||
$key = $this->get_rate_limit_key($action);
|
||||
$data = get_transient($key);
|
||||
|
||||
if ($data === false) {
|
||||
// First request in this window
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'start' => time(),
|
||||
];
|
||||
} else {
|
||||
$data['count']++;
|
||||
}
|
||||
|
||||
// Set/update transient with remaining window time
|
||||
$elapsed = time() - $data['start'];
|
||||
$remaining = max(1, $this->window - $elapsed);
|
||||
|
||||
set_transient($key, $data, $remaining);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check rate limit and record request in one call.
|
||||
*
|
||||
* @param string $action The action being checked.
|
||||
* @return bool True if request is allowed, false if rate limited.
|
||||
*/
|
||||
public function check_and_record($action) {
|
||||
if ($this->is_limited($action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->record_request($action);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of remaining requests in the current window.
|
||||
*
|
||||
* @param string $action The action to check.
|
||||
* @return int Number of remaining requests.
|
||||
*/
|
||||
public function get_remaining($action) {
|
||||
$key = $this->get_rate_limit_key($action);
|
||||
$data = get_transient($key);
|
||||
|
||||
if ($data === false) {
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
return max(0, $this->limit - $data['count']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rate limit key for the current user/IP.
|
||||
*
|
||||
* @param string $action The action being rate limited.
|
||||
* @return string Transient key.
|
||||
*/
|
||||
private function get_rate_limit_key($action) {
|
||||
// Use user ID if logged in, otherwise IP
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
if ($user_id > 0) {
|
||||
$identifier = 'user_' . $user_id;
|
||||
} else {
|
||||
// Sanitize and hash IP for privacy
|
||||
$ip = $this->get_client_ip();
|
||||
$identifier = 'ip_' . md5($ip);
|
||||
}
|
||||
|
||||
return 'mlf_rate_' . sanitize_key($action) . '_' . $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client IP address.
|
||||
*
|
||||
* @return string Client IP address.
|
||||
*/
|
||||
private function get_client_ip() {
|
||||
$ip = '';
|
||||
|
||||
// Check for various headers (in order of reliability)
|
||||
$headers = [
|
||||
'HTTP_CLIENT_IP',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_X_FORWARDED',
|
||||
'HTTP_X_CLUSTER_CLIENT_IP',
|
||||
'HTTP_FORWARDED_FOR',
|
||||
'HTTP_FORWARDED',
|
||||
'REMOTE_ADDR',
|
||||
];
|
||||
|
||||
foreach ($headers as $header) {
|
||||
if (!empty($_SERVER[$header])) {
|
||||
// X-Forwarded-For can contain multiple IPs, get the first one
|
||||
$ips = explode(',', sanitize_text_field(wp_unslash($_SERVER[$header])));
|
||||
$ip = trim($ips[0]);
|
||||
|
||||
// Validate IP
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to localhost if no valid IP found
|
||||
return $ip ?: '127.0.0.1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear rate limit for a specific action and user/IP.
|
||||
*
|
||||
* @param string $action The action to clear.
|
||||
* @return void
|
||||
*/
|
||||
public function clear($action) {
|
||||
$key = $this->get_rate_limit_key($action);
|
||||
delete_transient($key);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue