@nqounetです。
前回は侵入イベントをコンソールに表示する最小構成を作りました。シンプルで良いですね。
でも、現実の運用では「ログを表示するだけ」では物足りません。今回は「脅威スコアも計算したい」という要望に応えようとして、コードが崩壊していく様子を見ていきましょう(意図的に破綻させるので安心してください)。
脅威スコアを追加したい
侵入イベントには危険度があります。SSHブルートフォースは要注意だし、ポートスキャンは「様子見」程度。この違いを数値化した「脅威スコア」を計算したくなりました。
素直に機能追加してみましょう。
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
| #!/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,
);
package main;
# 脅威スコア計算用のハッシュ
my %threat_scores = (
'SSH Brute Force' => 80,
'Port Scan' => 30,
'SQL Injection Attempt' => 90,
'XSS Attack' => 70,
);
my $total_score = 0;
my @events = (
IntrusionEvent->new(
timestamp => '2026-01-18T06:00:00+09:00',
source_ip => '192.168.1.100',
attack_type => 'SSH Brute Force',
),
IntrusionEvent->new(
timestamp => '2026-01-18T06:01:15+09:00',
source_ip => '10.0.0.55',
attack_type => 'Port Scan',
),
);
for my $event (@events) {
# ログ出力
say "=== 侵入イベント検知 ===";
say "時刻: " . $event->timestamp;
say "発信元: " . $event->source_ip;
say "攻撃種別: " . $event->attack_type;
# 脅威スコア計算
my $score = $threat_scores{$event->attack_type} // 50;
$total_score += $score;
say "脅威スコア: $score (累計: $total_score)";
say "";
}
|
動きますね。でも、このコード、イヤな予感がしませんか?
要望はさらに増える
「アラート機能も追加してほしい」と言われました。脅威スコアが一定以上になったら警告を出す機能です。
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
| #!/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,
);
package main;
my %threat_scores = (
'SSH Brute Force' => 80,
'Port Scan' => 30,
'SQL Injection Attempt' => 90,
'XSS Attack' => 70,
);
my $total_score = 0;
my $alert_threshold = 100;
my @events = (
IntrusionEvent->new(
timestamp => '2026-01-18T06:00:00+09:00',
source_ip => '192.168.1.100',
attack_type => 'SSH Brute Force',
),
IntrusionEvent->new(
timestamp => '2026-01-18T06:01:15+09:00',
source_ip => '10.0.0.55',
attack_type => 'Port Scan',
),
);
for my $event (@events) {
# ログ出力
say "=== 侵入イベント検知 ===";
say "時刻: " . $event->timestamp;
say "発信元: " . $event->source_ip;
say "攻撃種別: " . $event->attack_type;
# 脅威スコア計算
my $score = $threat_scores{$event->attack_type} // 50;
$total_score += $score;
say "脅威スコア: $score (累計: $total_score)";
# アラートチェック
if ($total_score >= $alert_threshold) {
say "!!! 警告: 脅威レベルが閾値を超えました !!!";
}
say "";
}
|
動作結果はこうなります。
1
2
3
4
5
6
7
8
9
10
11
12
| === 侵入イベント検知 ===
時刻: 2026-01-18T06:00:00+09:00
発信元: 192.168.1.100
攻撃種別: SSH Brute Force
脅威スコア: 80 (累計: 80)
=== 侵入イベント検知 ===
時刻: 2026-01-18T06:01:15+09:00
発信元: 10.0.0.55
攻撃種別: Port Scan
脅威スコア: 30 (累計: 110)
!!! 警告: 脅威レベルが閾値を超えました !!!
|
機能的には動いています。でも、コードをよく見てください。
何が問題なのか
このコードには致命的な設計上の問題があります。
問題1: 1つのループで全部やりすぎ
イベント処理のループの中で以下のことをすべてやっています。
- ログ出力
- 脅威スコア計算
- 累計スコア管理
- アラートチェック
これは「単一責任の原則(SRP)」に違反しています。1つの場所で複数の責務を担当すると、変更が難しくなります。
問題2: 通知先を追加するたびにコードを変更
もし「メール通知も追加して」と言われたら?ループの中にメール送信処理を追加することになります。
もし「Slack通知も追加して」と言われたら?さらにSlack送信処理を追加…
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
| # こうなる未来が見える
for my $event (@events) {
# ログ出力
say "...";
# 脅威スコア計算
my $score = ...;
# アラートチェック
if ($total_score >= $alert_threshold) {
say "!!!警告!!!";
}
# メール通知(追加)
if ($should_send_email) {
send_email($event);
}
# Slack通知(追加)
if ($should_send_slack) {
send_slack($event);
}
# LINE通知(追加)...もう勘弁して
}
|
これは「開放閉鎖の原則(OCP)」に違反しています。新しい機能を追加するたびに既存コードを変更しなければなりません。
今回のまとめ
今回は「通知先を増やしたい」という素直な要望に応えた結果、コードが破綻していく様子を見ました。
- 1つのループで複数の責務を担当(SRP違反)
- 通知先追加のたびに既存コードを変更(OCP違反)
このままでは保守性が悪く、バグも混入しやすくなります。
次回予告
次回はこの問題を解決するために「通知係を分離」します。ログ担当、スコア担当をそれぞれ別のクラスにすることで、責務を明確に分けていきます。
設計がスッキリしていく過程をお楽しみに。