Skip to content

Instantly share code, notes, and snippets.

@Kreshnik
Last active March 29, 2022 11:00
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Kreshnik/e119de17cffd243642c4 to your computer and use it in GitHub Desktop.
Save Kreshnik/e119de17cffd243642c4 to your computer and use it in GitHub Desktop.
Laravel 5 - PreventReplayAttack Middleware

#Laravel 5 - PreventReplayAttack Middleware

Protect your app from Replay Attacks. The middleware uses an identifier together with the laravel cache to know which user has made a request. Currently you can specify wherever you want to use the session token as an unique identifier or chose an input field from which the the identifier is taken.

Installation

Simply copy the file across into the appropriate directory, and register the middleware in App\Http\Kernel.php. Either register it as a global HTTP middleware, or a route middleware. Alter variables within the middleware so they fit your needs, and you are good to go!

<?php namespace App\Http\Middleware;
use Carbon\Carbon;
use Closure;
use Illuminate\Cache\Repository as Cache;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Session;
class PreventReplayAttack
{
/**
* Variable that holds the cache driver.
* @var Cache
*/
private $cache;
/**
* Seconds between each request.
* @var secondsBetweenRequest
*/
private $secondsBetweenRequest = 5;
/**
* Cache key prefix.
* @var prefix
*/
private $prefix = "caller_";
/**
* Input field that holds the user identifier.
* @var inputIdentifier
*/
private $inputIdentifier = 'session_token';
/**
* Specify wherever to use Session::token() or inputIdentifier.
* @var useSessionToken
*/
private $useSessionToken = false;
/**
* The constructor.
* @param Cache $cache
*/
public function __construct( Cache $cache )
{
$this->cache = $cache;
}
/**
* Handle an incoming request.
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle( $request, Closure $next )
{
if ( $this->isRequestToFast($request) )
return Response::json(["error" => "You are performing an replay attack."]);
return $next($request);
}
/**
* Check if the request was to fast.
* @param $request
* @return bool
* @throws \Exception
*/
private function isRequestToFast( $request )
{
if ( !$this->useSessionToken && !$request->input($this->inputIdentifier) )
throw new \Exception("The input identifier cannot be empty.");
$key = $this->getCacheKey($request);
if ( $this->cache->has($key) ) return true;
$this->cache->put($key, Carbon::now(), ($this->getExpiration()));
return false;
}
/**
* Get the expiration seconds.
* @return float
*/
private function getExpiration()
{
return ($this->secondsBetweenRequest / 60);
}
/**
* Generate the cache key.
* @param $request
* @return string
*/
private function getCacheKey( $request )
{
$suffix = ($this->useSessionToken) ? Session::token() : $request->input($this->inputIdentifier);
return $this->prefix . $suffix;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment