Featured image of post 第9回-完成!侵入レーダー司令室 - Perlでハニーポット侵入レーダーを作ろう

第9回-完成!侵入レーダー司令室 - Perlでハニーポット侵入レーダーを作ろう

すべてのObserverを統合し、ハニーポット侵入レーダーを完成させます。ログ・スコア・リスクレベル・アラートが連動する司令室の完成コードをお届けします。

@nqounetです。

前回は開放閉鎖の原則(OCP)を実践し、既存コードを変更せずにRiskLevelObserverを追加しました。

今回はいよいよ完成版です。これまで作ってきたすべてのObserverを統合し、ハニーポット侵入レーダーの司令室を完成させます。

ここまでよく頑張りました!(自分を褒めてあげてください)

完成版コード

  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
#!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo(cpanmでインストール)

use v5.36;

#--------------------------------------------------
# イベントクラス
#--------------------------------------------------
package IntrusionEvent;
use Moo;

has timestamp => (
    is       => 'ro',
    required => 1,
);

has source_ip => (
    is       => 'ro',
    required => 1,
);

has attack_type => (
    is       => 'ro',
    required => 1,
);

#--------------------------------------------------
# Observer契約(Role)
#--------------------------------------------------
package IntrusionObserver;
use Moo::Role;

requires 'update';

#--------------------------------------------------
# 各種Observer
#--------------------------------------------------
package RadarLogObserver;
use Moo;
with 'IntrusionObserver';

sub update ($self, $event) {
    say "[LOG] " . $event->timestamp;
    say "      Type: " . $event->attack_type;
    say "      From: " . $event->source_ip;
}

package ThreatScoreObserver;
use Moo;
with 'IntrusionObserver';

my %threat_scores = (
    'SSH Brute Force'       => 80,
    'Port Scan'             => 30,
    'SQL Injection Attempt' => 90,
    'XSS Attack'            => 70,
    'Directory Traversal'   => 60,
);

has total_score => (
    is      => 'rw',
    default => 0,
);

sub update ($self, $event) {
    my $score = $threat_scores{$event->attack_type} // 50;
    $self->total_score($self->total_score + $score);
    say "[SCORE] +" . $score . " = " . $self->total_score . " total";
}

package RiskLevelObserver;
use Moo;
with 'IntrusionObserver';

my %risk_levels = (
    'SSH Brute Force'       => 'HIGH',
    'Port Scan'             => 'LOW',
    'SQL Injection Attempt' => 'HIGH',
    'XSS Attack'            => 'MEDIUM',
    'Directory Traversal'   => 'MEDIUM',
);

sub update ($self, $event) {
    my $level = $risk_levels{$event->attack_type} // 'MEDIUM';
    my $bar = $level eq 'HIGH'   ? '████████████████'
            : $level eq 'MEDIUM' ? '████████████'
            : '████████';
    say "[RISK] $bar $level";
}

package AlertObserver;
use Moo;
with 'IntrusionObserver';

sub update ($self, $event) {
    say "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
    say "!!! ALERT: Intrusion Detected !!!";
    say "!!! " . $event->attack_type . " from " . $event->source_ip;
    say "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
}

#--------------------------------------------------
# 司令塔(Subject)
#--------------------------------------------------
package IntrusionHub;
use Moo;

has observers => (
    is      => 'ro',
    default => sub { [] },
);

sub attach ($self, $observer) {
    unless ($observer->does('IntrusionObserver')) {
        die "Error: IntrusionObserverロールを実装していません";
    }
    push $self->observers->@*, $observer;
}

sub detach ($self, $observer) {
    $self->observers->@* = grep { $_ != $observer } $self->observers->@*;
}

sub notify ($self, $event) {
    for my $observer ($self->observers->@*) {
        $observer->update($event);
    }
}

#--------------------------------------------------
# メイン処理
#--------------------------------------------------
package main;

say "╔══════════════════════════════════════════════╗";
say "║   HONEYPOT INTRUSION RADAR - COMMAND CENTER  ║";
say "╚══════════════════════════════════════════════╝";
say "";

# 司令塔を作成
my $hub = IntrusionHub->new;

# 常駐Observer
$hub->attach(RadarLogObserver->new);
$hub->attach(ThreatScoreObserver->new);
$hub->attach(RiskLevelObserver->new);

# 深夜帯用(今回はデモ用に常時ON)
my $alert = AlertObserver->new;
$hub->attach($alert);

# 侵入イベントをシミュレート
my @events = (
    IntrusionEvent->new(
        timestamp   => '2026-01-18T02:15:33+09:00',
        source_ip   => '203.0.113.42',
        attack_type => 'SSH Brute Force',
    ),
    IntrusionEvent->new(
        timestamp   => '2026-01-18T02:17:45+09:00',
        source_ip   => '198.51.100.77',
        attack_type => 'Port Scan',
    ),
    IntrusionEvent->new(
        timestamp   => '2026-01-18T02:20:12+09:00',
        source_ip   => '192.0.2.123',
        attack_type => 'SQL Injection Attempt',
    ),
);

for my $event (@events) {
    say "──────────────────────────────────────────────";
    $hub->notify($event);
    say "";
}

say "──────────────────────────────────────────────";
say "[RADAR] All events processed.";

実行結果

 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
╔══════════════════════════════════════════════╗
║   HONEYPOT INTRUSION RADAR - COMMAND CENTER  ║
╚══════════════════════════════════════════════╝

──────────────────────────────────────────────
[LOG] 2026-01-18T02:15:33+09:00
      Type: SSH Brute Force
      From: 203.0.113.42
[SCORE] +80 = 80 total
[RISK] ████████████████ HIGH
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!! ALERT: Intrusion Detected !!!
!!! SSH Brute Force from 203.0.113.42
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

──────────────────────────────────────────────
[LOG] 2026-01-18T02:17:45+09:00
      Type: Port Scan
      From: 198.51.100.77
[SCORE] +30 = 110 total
[RISK] ████████ LOW
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!! ALERT: Intrusion Detected !!!
!!! Port Scan from 198.51.100.77
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

──────────────────────────────────────────────
[LOG] 2026-01-18T02:20:12+09:00
      Type: SQL Injection Attempt
      From: 192.0.2.123
[SCORE] +90 = 200 total
[RISK] ████████████████ HIGH
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!! ALERT: Intrusion Detected !!!
!!! SQL Injection Attempt from 192.0.2.123
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

──────────────────────────────────────────────
[RADAR] All events processed.

達成したこと

このシリーズを通じて、以下のことを達成しました。

  • IntrusionEvent: 侵入イベントをモデル化
  • IntrusionObserver: Observer契約をRoleで定義
  • RadarLogObserver: ログ出力担当
  • ThreatScoreObserver: 脅威スコア計算担当
  • RiskLevelObserver: リスクレベル判定担当
  • AlertObserver: アラート通知担当
  • IntrusionHub: 司令塔(全Observerの一元管理)

各Observerは自分の責務だけに集中し(SRP)、新しい機能は既存コードを変更せずに追加できます(OCP)。

今回のまとめ

ハニーポット侵入レーダーの司令室が完成しました。

  • すべてのObserverが連動して動作
  • イベント1つで複数の処理が自動実行
  • 柔軟な拡張性を維持

達成感ありますね。

次回予告

最終回は「これがObserverパターンだ!」です。ここまで作ってきた設計が、実はGoFの有名なデザインパターンだったことを明かします。

パターンの全体像と応用範囲を確認しましょう。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。