优化检查 url 段问题的方法
Optimize a method that checks url segments issue
我一直在 Laravel 中间件中使用一种方法来检查任何 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;
}
}
我不知道这样做是否会优化代码,但我认为代码更具可读性。
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)
{
//loop over the list instead of that long conditions
foreach($this->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 的新阅读,因此该新文件中的任何路由都会 ban/block IP。
我相信你可以使用 DB 并在 DB 中将黑名单设为 indexed,
数据库将使用其内部引擎自行处理和搜索。
protected function urlWasInBlackList($segment = -1){
$segmentStrings= $segment != -1 ? [request()->segment($segment)] : request()->segments();
return DB::table('blacklisted')->select('*')->whereIn('pattern',$segmantStrings)->exists()
}
并使用类似这样的函数代替 inUrl()
。它将检查 Against DB 的禁止词。
更新:避免数据库
但是 DB 使用其 InnoDB 引擎快速处理这种情况,
也许你想避免数据库,因为连接开销或者只是因为一个功能而不依赖你的项目,
如果你想避免数据库,
你必须检查 url 每个 黑名单词 与 循环 并且它将是 O(n)
,
如果你乘以 节计数 ,它将是 O(n*m)
。
最佳方法是避免循环,并像 DB 那样制作 散列 table。
所以我在 php 中寻找 hash_tables 并在 php array doc 中找到
php 关联数组是 哈希映射 。
并且函数 array_key_exists()
正在寻找键的散列 table,您可以在 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);
}
在所有的建议之后,请在这里张贴,我最终得到了一个使用一些建议方法的解决方案。
结果是将页面加载时间减少了 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',
//...
];
];
- 优化了中间件,删除了
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% 以上。
感谢所有帮助我解决问题的建议方法。
我一直在 Laravel 中间件中使用一种方法来检查任何 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;
}
}
我不知道这样做是否会优化代码,但我认为代码更具可读性。
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)
{
//loop over the list instead of that long conditions
foreach($this->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 的新阅读,因此该新文件中的任何路由都会 ban/block IP。
我相信你可以使用 DB 并在 DB 中将黑名单设为 indexed, 数据库将使用其内部引擎自行处理和搜索。
protected function urlWasInBlackList($segment = -1){
$segmentStrings= $segment != -1 ? [request()->segment($segment)] : request()->segments();
return DB::table('blacklisted')->select('*')->whereIn('pattern',$segmantStrings)->exists()
}
并使用类似这样的函数代替 inUrl()
。它将检查 Against DB 的禁止词。
更新:避免数据库
但是 DB 使用其 InnoDB 引擎快速处理这种情况, 也许你想避免数据库,因为连接开销或者只是因为一个功能而不依赖你的项目,
如果你想避免数据库,
你必须检查 url 每个 黑名单词 与 循环 并且它将是 O(n)
,
如果你乘以 节计数 ,它将是 O(n*m)
。
最佳方法是避免循环,并像 DB 那样制作 散列 table。
所以我在 php 中寻找 hash_tables 并在 php array doc 中找到
php 关联数组是 哈希映射 。
并且函数 array_key_exists()
正在寻找键的散列 table,您可以在 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);
}
在所有的建议之后,请在这里张贴,我最终得到了一个使用一些建议方法的解决方案。
结果是将页面加载时间减少了 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',
//...
];
];
- 优化了中间件,删除了
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% 以上。
感谢所有帮助我解决问题的建议方法。