Initial commit - CMS Gov Bapenda Garut dengan EditorJS
This commit is contained in:
95
app/Filters/ThrottleFilter.php
Normal file
95
app/Filters/ThrottleFilter.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
class ThrottleFilter implements FilterInterface
|
||||
{
|
||||
/**
|
||||
* Rate limit configuration
|
||||
* Production: 10 requests per minute
|
||||
* Development: 30 requests per minute (still active for security testing)
|
||||
*/
|
||||
protected function getRateLimit(): int
|
||||
{
|
||||
return ENVIRONMENT === 'production' ? 10 : 30;
|
||||
}
|
||||
|
||||
protected function getWindowSeconds(): int
|
||||
{
|
||||
return 60; // 1 minute window
|
||||
}
|
||||
|
||||
/**
|
||||
* Do whatever processing this filter needs to do.
|
||||
*/
|
||||
public function before(RequestInterface $request, $arguments = null)
|
||||
{
|
||||
// Only throttle POST requests to login
|
||||
if (strtolower($request->getMethod()) !== 'post') {
|
||||
return;
|
||||
}
|
||||
|
||||
$ipAddress = $request->getIPAddress();
|
||||
$cache = \Config\Services::cache();
|
||||
|
||||
// Use a more specific key for login endpoint
|
||||
$path = $request->getUri()->getPath();
|
||||
$key = 'throttle_login_' . md5($ipAddress . '_' . $path);
|
||||
|
||||
$current = $cache->get($key);
|
||||
|
||||
// Log for debugging
|
||||
log_message('debug', "Throttle check - IP: {$ipAddress}, Path: {$path}, Current: " . ($current ?? 'null') . ", Limit: {$this->getRateLimit()}");
|
||||
|
||||
if ($current === null) {
|
||||
// First request - initialize counter
|
||||
$cache->save($key, 1, $this->getWindowSeconds());
|
||||
log_message('debug', "Throttle initialized for IP: {$ipAddress}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Increment counter
|
||||
$newCount = $current + 1;
|
||||
$cache->save($key, $newCount, $this->getWindowSeconds());
|
||||
|
||||
log_message('debug', "Throttle incremented - IP: {$ipAddress}, Count: {$newCount}, Limit: {$this->getRateLimit()}");
|
||||
|
||||
// Check if limit exceeded (use >= instead of > to be more strict)
|
||||
if ($newCount >= $this->getRateLimit()) {
|
||||
log_message('warning', "Rate limit exceeded for IP: {$ipAddress} on path: {$path} - Count: {$newCount}, Limit: {$this->getRateLimit()}");
|
||||
|
||||
// Create response with 429 status
|
||||
$response = service('response');
|
||||
$response->setStatusCode(429);
|
||||
$response->setBody('Too Many Requests. Please try again later.');
|
||||
$response->setHeader('Retry-After', (string) $this->getWindowSeconds());
|
||||
$response->setHeader('X-RateLimit-Limit', (string) $this->getRateLimit());
|
||||
$response->setHeader('X-RateLimit-Remaining', '0');
|
||||
$response->setHeader('X-RateLimit-Reset', (string) (time() + $this->getWindowSeconds()));
|
||||
$response->setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
$response->setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Set rate limit headers for successful requests
|
||||
$remaining = max(0, $this->getRateLimit() - $newCount);
|
||||
$response = service('response');
|
||||
$response->setHeader('X-RateLimit-Limit', (string) $this->getRateLimit());
|
||||
$response->setHeader('X-RateLimit-Remaining', (string) $remaining);
|
||||
$response->setHeader('X-RateLimit-Reset', (string) (time() + $this->getWindowSeconds()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows After filters to inspect and modify the response
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
|
||||
{
|
||||
// No action needed after request
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user