laravelsecurityoptimizationmiddlewareblacklist

Optimize a method that checks url segments issue


I've been using a method in Laravel Middleware that checks for strings in any URL segment to block the IP if it matches the "blacklisted" strings.

In the beginning, I had just a few strings to check, but now, the list is growing, and when I tried to optimize it to use a blacklist array, I ended up in a complete mess in the code and in my mind.

I believe this can be done but can't figure out the best way to optimize this middleware. Below is a sample of the Middleware code with notes where I'm having trouble.

In the handle($request, Closure $next) method is calling the $this->inUrl() method for all the blacklisted strings.

I've tried to add a protected $blacklisted array, to be used in the $this->inUrl() but can't make it work.

Thank you in advance for any suggestions that would be much appreciated and welcome. I am also thinking of providing the code as a gist on GitHub when optimized.

namespace App\Http\Middleware;

/**
 * Class VerifyBlacklistedRequests
 *
 * @package App\Http\Middleware
 */
class VerifyBlacklistedRequests
{

    /**
     * The array of blacklisted request string segments
     *
     * @access protected
     * @var array|string[]
     */
    protected array $blacklisted = [
        '.env', '.ftpconfig', '.vscode', ',git', '.git/HEAD'
        // etc...
    ];

    /**
     * Handle an incoming request.
     *
     * @access public
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure                 $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if($this->inUrl('.env')
            || $this->inUrl('.ftpconfig')
            || $this->inUrl('.vscode')
            || $this->inUrl('.git')
            || $this->inUrl('.git/HEAD')
 
           // many more checks below the above ones

        ) {
            // logic that blocks the IP goes here and working fine
        }

        return $next($request);
    }

     /**
     * Check if the string is in any URL segment or at the one specified.
     *
     * @access protected
     *
     * @param string|mixed $value   Segment value/content.
     * @param integer      $segment Segment position.
     *
     * @return bool
     */
    protected function inUrl(string $value, $segment = -1)
    {
        if($segment !== -1 && request()->segment($segment) === $value) {
            return true;
        }

        collect(request()->segments())->each(function ($segment) use ($value) {
            if($segment === $value) {
                return true;
            }
        });

        return false;
    }

}

Solution

  • After all the suggestions, kindly posted here, I ended up with a solution that uses some of the suggested methods.

    The result ended up by reducing the pages' loading time by more than 1 second.

    My final implementation:

    1. Created a config file security.php which contains the blacklisted request strings, and a shortlist of whitelisted IPs.

    The security.php config file

    <?php
    return [
        /*
        |--------------------------------------------------------------------------
        | Whitelisted IPs configuration
        |--------------------------------------------------------------------------
        |
        | These are the settings for the whitelisted IPs. The array contains
        | the IPs that should not trigger the IP block.
        |
        */
        'whitelisted_ips' => [
            // whitelisted IPs array
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Blacklisted request strings configuration
        |--------------------------------------------------------------------------
        |
        | These are the settings for the blacklisted request strings. The array contains
        | the strings that should trigger the IP to be blocked.
        |
        */
        'blacklisted_requests' => [
            '.env',
            '.ftpconfig',
            '.vscode',
            '.git',
            '.git/HEAD',
            '_profiler',
            '__media__',
            'administrator',
            //...
        ];
    ];
    
    1. Optimized the middleware removing the loops on the inUrl() method

    The VerifyBlacklistedRequests middleware

    <?php
    namespace App\Http\Middleware;
    
    use Closure;
    
    /**
     * Class VerifyHackingAttemptsRequests
     *
     * @property \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed white_listed_ips
     * @property \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed blacklist
     * @package App\Http\Middleware
     */
    class VerifyHackingAttemptsRequests
    {
        /**
         * @access protected
         * @var \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed
         */
        protected $blacklist;
    
        /**
         * @access protected
         * @var \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed
         */
        protected $white_listed_ips;
    
        /**
         * VerifyHackingAttemptsRequests constructor
         *
         * @access public
         */
        public function __construct()
        {
            $this->blacklist        = config('security.blacklisted_requests');
            $this->white_listed_ips = config('security.whitelisted_ips');
        }
    
        /**
         * Handle an incoming request.
         *
         * @access public
         *
         * @param \Illuminate\Http\Request $request
         * @param \Closure                 $next
         *
         * @return mixed
         * @since  2.8.1
         */
        public function handle($request, Closure $next)
        {
            $exists = false;
    
            foreach(request()->segments() as $segment) {
                if(in_array($segment, $this->blacklist)) {
                    $exists = true;
                }
            }
    
            if($exists) {
                $this->blockIp($request)
            }
    
            return $next($request);
        }
    
        /**
         * Method to save an IP in the Blocked IP database table
         *
         * @access protected
         *
         * @param \Illuminate\Http\Request $request
         *
         * @return \App\Models\BlockedIp
         */
        protected function blockIp(Request $request, $notes = null)
        {
            // the logic to persist the data through the BlockedIp model
        }
    }
    

    In summary, the inUrl() method was removed, removing all the loops and method calls and, as mentioned above, the pages' loading time was sliced by more than 50%.

    Thanks to all for the suggested methods which contributed to helping me solve the problem.