优化检查url段问题的方法

问题描述

我一直在 Laravel Middleware 中使用一种方法,该方法检查任何 URL 段中的字符串,如果它与“黑名单”字符串匹配,则阻止该 IP。

一开始,我只有几个字符串要检查,但现在,列表越来越多,当我尝试优化它以使用 blacklist array 时,我的代码完全一团糟在我心中。

我相信这可以做到,但无法找出优化此中间件的最佳方法。下面是中间件代码示例,并附有我遇到问题的注释。

handle($request,Closure $next) 方法中为所有列入黑名单的字符串调用 $this->inUrl() 方法

我尝试添加一个 protected $blacklisted 数组,用于 $this->inUrl() 但无法使其工作。

预先感谢您提出的任何建议,我们将不胜感激和欢迎。我也在考虑在优化时在 GitHub 上提供代码作为要点。

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;
    }

}

解决方法

在所有的建议之后,好心地张贴在这里,我最终得到了一个使用一些建议方法的解决方案。

结果是将页面加载时间减少了 1 秒以上。

我的最终实现:

  1. 创建了一个配置文件 security.php,其中包含列入黑名单的请求字符串和列入白名单的 IP 候选名单。

security.php 配置文件

<?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. 优化中间件,去除 inUrl() 方法上的循环

VerifyBlacklistedRequests 中间件

<?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
    }
}

总而言之,inUrl() 方法被移除,移除了所有循环和方法调用,并且如上所述,页面的加载时间减少了 50% 以上。

感谢所有帮助我解决问题的建议方法。

,

我不知道这样做是否会优化代码,但我认为代码更具可读性。

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',',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)
    {
        //loop over the list instead of that long conditions
        foreach($blacklisted as $blacklistedItem)
        if($this->inUrl($blacklistedItem)) 
        {
            // logic that blocks the IP goes here and working fine
        }
    }

     /**
     * 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;
        }

        foreach(request()->segments() as $segment) {
            if($segment === $value) {
                return true;
            }
        }

        return false;
    }

}
,

我建议你创建文字路由,这样更容易维护。转到 RouteServiceProvider 并创建一个类似于 web 或 api 的新读取,因此该新文件中的任何路由都会禁止/阻止 IP。

,

我相信您可以使用数据库并将黑名单设为索引在数据库中, 数据库将使用其内部引擎自行处理和搜索。

protected function urlWasInBlackList($segment = -1){
    $segmentStrings= $segment != -1 ? [request()->segment($segment)] : request()->segments();

    return DB::table('blacklisted')->select('*')->whereIn('pattern',$segmantStrings)->exists()
}

并使用类似这样的函数而不是 inUrl()。它将检查针对 DB 的禁止词。


更新:避免数据库

然而,DB 使用其 InnoDB 引擎快速处理这种情况, 也许您想避免使用 DB,因为连接开销或仅仅因为一个功能而不想依赖您的项目,

如果你想避免数据库, 你必须用循环检查每个黑名单词的网址,它会是O(n), 如果你乘以节数,它会是O(n*m)

最好的方法是避免循环,并像 DB 那样制作哈希表

所以我在 php 中查找 hash_tables 并在 php array doc php 关联数组是哈希映射。 并且函数 array_key_exists() 正在查看键的哈希表,您可以在 php 源代码中看到 array_key_exists() 代码,这些代码将 ht 寻址为这里的哈希映射:

array.c 行 #6071

zen_hash.h 第 529 行

所以我建议使用这样的东西:

protected array $blacklisted = [
        '.env' => null,'.ftpconfig' => null,'.vscode' => null,git' => null,'.git/HEAD' => null
      
    ];

用于黑名单定义。 和

    $segmentStrings= $segment != -1 ? [request()->segment($segment)] : request()->segments();

foreach(segmentStrings as $segmentString){
    return array_key_exists($segmentString,$blacklisted);
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...