Skip to content

Instantly share code, notes, and snippets.

@dereuromark
Created March 11, 2024 17:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dereuromark/6e47bcdbb42915af3f51c838cd65ef4f to your computer and use it in GitHub Desktop.
Save dereuromark/6e47bcdbb42915af3f51c838cd65ef4f to your computer and use it in GitHub Desktop.
BlackholeMiddleware.php
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Cake\Core\Exception\CakeException;
use Cake\Core\InstanceConfigTrait;
use Cake\Http\Response;
use Cake\Log\Log;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class BlackholeMiddleware implements MiddlewareInterface
{
use InstanceConfigTrait;
/**
* Configuration options.
*
* @var array<string, mixed>
*/
protected array $_defaultConfig = [
];
/**
* The amount of time to cache the asset.
*
* @var string
*/
protected string $cacheTime = '+1 day';
/**
* Constructor.
*
* @param array<string, mixed> $options The options to use
*/
public function __construct(array $options = [])
{
if (!empty($options['cacheTime'])) {
$this->cacheTime = $options['cacheTime'];
}
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request The request.
* @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler.
*
* @return \Psr\Http\Message\ResponseInterface A response.
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($this->isBlackholed($request)) {
Log::write('debug', 'BlackholeMiddleware: blackholed ' . env('REMOTE_ADDR'));
return $this->respond();
}
return $handler->handle($request);
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
*
* @return bool
*/
protected function isBlackholed(ServerRequestInterface $request): bool
{
$list = [
'...',
];
$ip = env('REMOTE_ADDR');
if (in_array($ip, $list, true)) {
return true;
}
return false;
}
/**
* Respond with the 403 code.
*
* @throws \Cake\Core\Exception\CakeException
*
* @return \Psr\Http\Message\ResponseInterface
*/
protected function respond(): ResponseInterface
{
$expire = strtotime($this->cacheTime);
if ($expire === false) {
throw new CakeException(sprintf('Invalid cache time value `%s`', $this->cacheTime));
}
$maxAge = $expire - time();
$message = 'Spam bot or malicious (crawler) request';
$response = new Response();
$body = $response->getBody();
$body->write($message);
$body->rewind();
return $response
->withStatus(403, $message)
->withHeader('Cache-Control', 'public,max-age=' . $maxAge)
->withHeader('Date', gmdate(DATE_RFC7231, time()))
->withHeader('Expires', gmdate(DATE_RFC7231, $expire));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment