Laravel:不正アクセスを撃退するLOG&Redirectの仕組みを作ってみた
目次
不正アクセスを撃退するLOG&Redirectの仕組み
システムを組んでいるとマジでムカついてくる不正アクセス。
なぜこうも不正アクセスが多いのかホント腹立たしく感じています。
と言う事で、以前の記事で攻撃を受けるURLを書いたりLogに残ったIPから地域調べてみたりしましたが今回は撃退偏です。
まずは仕組みを考える
- アクセスlogでIPとアクセスURLの記録をする
- 攻撃対象となっている存在しないURLをブラックURLとして管理する
- ブラックURLに対してアクセスしてきたIPをブラックIPとして管理する
- ブラックIPでも、ブラックURLアクセス5回までは許してやる(出来心的な?許容範囲)
- ブラックIPがアクセスしたURLはブラックURLに登録する
- 許容範囲を超えたブラックIPは別の場所に勝手に飛んでもらう
はい、こんな感じで行きましょう。
必要となるテーブルを作る
欲しいテーブルは3つです。
- 基本的なアクセスログ(アクションログ) ⇒ table名:actlogs
- ブラックURLリスト ⇒ table名:blackurllists
- ブラックIPリスト ⇒ table名:blackiplists
MigrationとModel、Controllerの箱を作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#マイグレーション作成 php artisan make:migration create_actlogs_table php artisan make:migration create_blackurllists_table php artisan make:migration create_blackiplists_table #モデル作成 php artisan make:model Actlog php artisan make:model Blackurllist php artisan make:model Blackiplist #コントローラー作成 php artisan make:controller ActlogController --resource php artisan make:controller BlackurllistgController --resource php artisan make:controller BlackiplistController --resource |
マイグレーションファイルを編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#actlogのMigration <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateActlogsTable extends Migration { public function up() { Schema::create('actlogs', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned()->nullable()->comment('作業者ID'); $table->string('route')->nullable(); $table->string('url')->nullable(); $table->string('method')->nullable(); $table->integer('status')->unsigned()->nullable(); $table->text('message')->nullable(); $table->string('remote_addr')->nullable(); $table->string('user_agent')->nullable(); $table->timestamps(); $table->index(['created_at']); $table->index(['user_id']); }); } public function down() { Schema::dropIfExists('actlogs'); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#blackurllistのMigration <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBlackurllistsTable extends Migration { public function up() { Schema::create('blackaurllists', function (Blueprint $table) { $table->increments('id'); $table->string('accessurl',255)->comment('要チェックアクセス'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('blackaccesslists'); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#blackiplistのMigration <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBlackiplistsTable extends Migration { public function up() { Schema::create('blackiplists', function (Blueprint $table) { $table->increments('id'); $table->string('blackip',20)->comment('不正アクセスのあったIP'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('blackiplists'); } } |
マイグレーションを実行します。
1 |
php artisan migrate |
これでテーブルが出来ました。
Modelを修正する
■モデル:Actlog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace App\; use Illuminate\Database\Eloquent\Model; class Actlog extends Model { protected $guarded = ['*']; protected $casts = ['meta' => 'json']; const UPDATED_AT = null; public function __construct(array $attributes = []) { parent::__construct($attributes); $connection = env('DB_LOG_CONNECTION', env('DB_CONNECTION', 'mysql')); } protected $fillable = [ 'user_id', 'route', 'url', 'method', 'status', 'message', 'remote_addr', 'user_agent', ]; protected $hidden = [ ]; } |
テーブル:actlogsにはjsonがそのまま入る可能性があります。
protected $casts = [‘meta’ => ‘json’]; はその為の設定です。
BlackurllistとBlackiplist はartisanで作ったものそのままでOK。
Controllerの修正
Viewへつなげる設定だけ行って後はお好みでOK。
Log記載ミドルウェアを作成
1 |
php artisan make:middleware ActlogMiddleware |
作成したミドルウェアをkernelに登録します。
場所:Project >> app >> Http >> Middleware >> Kernel.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { protected $middleware = [ . . . \App\Http\Middleware\ActlogMiddleware::class, ]; . . . } |
さぁ、これでした準備は整いました。
いよいよ不正アクセス締め出しシステムの本丸『ミドルウェア:Actlog』の造作です。
ActlogMiddleware.phpを作る
こんな感じにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
<?php namespace App\Http\Middleware; use Closure; use App\Models\Actlog; use Route; use App\Models\Blackaccesslist; use App\Models\Blackiplist; use Carbon; class ActlogMiddleware { public function handle($request, Closure $next) { $response = $next($request); $this->actlog($request, $response->status()); return $response; } public function actlog($request, $status) { $user = $request -> user(); $data = [ 'user_id' => $user ? $user->id : null, 'route' => Route::currentRouteName(), 'url' => $request -> path(), 'method' => $request -> method(), 'status' => $status, 'message' => count($request->toArray()) != 0 ? json_encode($request->toArray()) : null, 'remote_addr' => $request -> ip(), 'user_agent' => $request -> userAgent(), ]; #ブラックIPに登録があるか確認 $BlackIp = Blackiplist::where('blackip',$data['remote_addr'])->first(); #アクセスチェック $BlackAccess = Blackurllist::where('accessurl',$data['url'])->where('onoff',0)->first(); #ログイン履歴があるか確認 $Actlogs = Actlog::where('remote_addr',$data['remote_addr'])->whereNotNull('user_id')->first(); #アクセス回数を取出す $visitcount = 0; if(!empty($BlackIp)){$visitcount = $BlackIp->count;} switch ($visitcount){ case 0: #ブラックIP値なし $vol = self::checkBlackAccess($data,$Actlogs,$BlackAccess); break; case 1: case 2: case 3: case 4: case 5: #ブラックIP値あり規定数以下 $vol = self::BlackCancelerPath($data,$Actlogs,$BlackIp,$BlackAccess); break; default : #ブラックIP値あり既定数以上 $vol = self::BlackCancelerNG($data,$Actlogs,$BlackIp,$BlackAccess); break; } switch($vol){ case 0: case 1: #通常処理 Actlog::create($data); self::ErrorPageSeparater($status); break; default : $input = array('https://google.co.jp','https://www.rakuten.ne.jp','https://amazon.cn'); #自動退場※勝手にリダイレクト return redirect(array_rand($input,1)); break; } } /* * ■ブラックIP値なし * 1. $Actlogに値がある時何もしない * 2. $Actlogに値が無い時 * 3.不正URLへのアクセスか確認 * 4.不正URLへのアクセスの時 * 5.ブラックIPに記載 * 6.不正URLへのアクセスではない時 * 7.何もしない */ public function checkBlackAccess($data,$Actlogs,$BlackAccess) { $value = 0; if(empty($Actlogs)){//正式ログイン履歴無し if(!empty($BlackAccess)){//不正URLへのアクセスあり $item = new Blackiplist; $item->blackip = $data['remote_addr']; $item->count = 1; $item->onoff = 0;//非許可で登録 $item->save(); $value = 1; } } return $value; } /* * ■ブラックIP値あり既定数以上 * 1. $Actlogに値がある時何もしない * 2. $Actlogに値が無い時 * 3.不正URLへのアクセスか確認 * 4.不正URLへのアクセスの時 * 5.ブラックIPの値を加算 * 6.不正URLを加算 * 7.不正URLへのアクセスではない時 * 7.不正URLにアクセスURLを記載※但しonoff=1で登録する */ public function BlackCancelerNG($data,$Actlogs,$BlackIp,$BlackAccess) { $value = 0; if(empty($Actlogs)){//正規ログイン履歴なし if(!empty($BlackAccess)){//不正URLへのアクセス #IP最終アクセスを更新 $item = Blackiplist::where('id',$BlackIp->id);//ブラックIPリストをサーチ $item->update(['count' => ($BlackIp->count)+1]); #URL最終アクセスを更新 $item = Blackurllist::where('id',$BlackAccess->id); $item->update(['count'=> ($BlackAccess->count)+1]); $value = 2; }else{ #念のため許可URLの記載も含めチェック ※'/'対策 $BlackAccess2 = Blackurllist::where('accessurl',$data['url'])->first(); if(!empty($BlackAccess2)){ #URL最終アクセスを更新 $item = Blackurllist::where('id',$BlackAccess2->id); $item->update(['count'=> ($BlackAccess2->count)+1]); }else{ #URLアクセスを新規作成 $item = new Blackurllist; $item->accessurl = $data['url']; $item->count = 1; $item->onoff = 1; $item->save(); } $value = 2; } } return $value; } /* * ■ブラックIP値あり既定数以下 * 1. $Actlogに値がある時何もしない * 2. $Actlogに値が無い時 * 3.不正URLへのアクセスか確認 * 4.不正URLへのアクセスの時 * 5.ブラックIPの値を加算 * 6.不正URLを加算 * 7.不正URLへのアクセスではない時 * 7.不正URLにアクセスURLを記載※但しonoff=1で登録する */ public function BlackCancelerPath($data,$Actlogs,$BlackIp,$BlackAccess) { $value = 0; if(empty($Actlogs)){ if(!empty($BlackAccess)){ #IP最終アクセスを更新 $item = Blackiplist::where('id',$BlackIp->id);//ブラックIPリストをサーチ $item->update(['count' => ($BlackIp->count)+1]); #URL最終アクセスを更新 $item = Blackurllist::where('id',$BlackAccess->id); $item->update(['count'=> ($BlackAccess->count)+1]); $value = 1; }else{ #念のため許可URLの記載も含めチェック ※'/'対策 $BlackAccess2 = Blackurllist::where('accessurl',$data['url'])->first(); if(!empty($BlackAccess2)){ #URL最終アクセスを更新 $item = Blackurllist::where('id',$BlackAccess2->id); $item->update(['count'=> ($BlackAccess2->count)+1]); }else{ #URLアクセスを新規作成 $item = new Blackurllist; $item->accessurl = $data['url']; $item->count = 1; $item->onoff = 1; $item->save(); } $value = 1; } } return $value; } #Errorフィルター public function ErrorPageSeparater($status) { switch($status){ case 400: return redirect('error/500'); break; case 401: return redirect('error/500'); break; case 403: return redirect('error/500'); break; case 404: return redirect('error/404'); break; case 405: return redirect('error/404'); break; case 419: return redirect('error/419'); break; case 500: return redirect('error/500'); break; case 503: return redirect('error/500'); break; case 999: return redirect('error/999'); break; default : break; } } } |
不正アクセス野郎だったら勝手にRedirect!
$input = array(‘https://google.co.jp’,’https://www.rakuten.ne.jp’,’https://amazon.cn’);
return redirect(array_rand($input,1));
return redirect(array_rand($input,1));
まとめ
これ作ってモニタリングしてると、500回越えの不正アクセスがざらにある事がわかります。
それもACCESS元調べれば大体が中国。セキュリティしっかりしないとですね。
-
前の記事
画面サイズに合わせた長さで文章を省略したい overflowの調整 2020.03.11
-
次の記事
DEMOサーバーにSSLを設定したので次回楽になる為のメモ その3 2020.03.13
コメントを残す