第5章: パケットフィルタリング理論とファイアウォールアーキテクチャ - ネットワーク防御の計算機科学的基盤
5.1 ファイアウォールの歴史的発展
ネットワークセキュリティの黎明期
1969年のARPANET誕生当初、ネットワークセキュリティは主要な懸念事項ではありませんでした。参加者は相互に信頼された研究機関であり、ネットワークは比較的小規模でした。
1980年代、インターネットの商用利用が始まると、状況が一変しました。1988年のMorris Wormはインターネットに接続された約6,000台のコンピュータ(当時の約10%)に感染し、ネットワークセキュリティの必要性を劇的に示しました。
> "Morris Wormは、インターネットが信頼に基づく設計であることを暴露した。" — Clifford Stoll, 1989
第一世代: パケットフィルタ(1988年)
Digital Equipment Corporation(DEC)のエンジニアは、最初のパケットフィルタリングファイアウォールを開発しました。
パケットフィルタの動作原理:
入力パケット
↓
┌─────────────────────────────────────┐
│ パケットヘッダ検査 │
│ ┌─────────────────────────────┐ │
│ │ 送信元IP: 192.168.1.100 │ │
│ │ 宛先IP: 10.0.2.15 │ │
│ │ プロトコル: TCP │ │
│ │ 送信元ポート: 54321 │ │
│ │ 宛先ポート: 22 │ │
│ └─────────────────────────────┘ │
│ ↓ │
│ ルールと照合 │
│ ↓ │
│ 許可 / 拒否 / ドロップ │
└─────────────────────────────────────┘
↓
出力(許可された場合)
特徴:
- ステートレス: 各パケットを独立して評価
- 高速: ヘッダのみを検査
- 単純: ルールが明確
制限:
- 接続の状態を追跡しない
- アプリケーション層を検査しない
- TCP接続の正当性を検証できない
第二世代: ステートフルインスペクション(1994年)
1994年、Check Point Software Technologies(Gil ShwedとShlomo Kramer)はステートフルインスペクションを発明しました。この技術はファイアウォールに革命をもたらしました。
ステートフルインスペクションの概念:
┌─────────────────────────────────────────────┐
│ 接続追跡テーブル │
│ ┌───────────────────────────────────────┐ │
│ │ 接続1: 192.168.1.100:54321 → │ │
│ │ 10.0.2.15:22 [ESTABLISHED] │ │
│ │ 接続2: 192.168.1.101:54322 → │ │
│ │ 10.0.2.15:80 [SYN_SENT] │ │
│ │ 接続3: 192.168.1.102:54323 → │ │
│ │ 10.0.2.15:443 [TIME_WAIT] │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
新しいパケット到着時:
1. 既存の接続に属するか確認
2. 属する場合: 接続状態に基づいて許可/拒否
3. 属さない場合: ルールに基づいて評価
利点:
- TCPの3ウェイハンドシェイクを追跡
- 不正なパケット(ACKなしでFIN等)を検出
- 応答パケットを自動的に許可
第三世代: アプリケーション層ファイアウォール
1990年代後半、プロキシファイアウォールとディープパケットインスペクション(DPI)が登場しました。
従来のファイアウォール:
Layer 3-4のみ検査(IP, TCP/UDP)
アプリケーション層ファイアウォール:
Layer 7まで検査(HTTP, FTP, DNS, etc.)
例: HTTPリクエストの検査
GET /admin/delete_user.php HTTP/1.1
Host: example.com
Cookie: session=malicious_script
→ SQLインジェクション検出
→ 不正なパスへのアクセス検出
→ 悪意のあるペイロード検出
現代のファイアウォール: 次世代ファイアウォール(NGFW)
2000年代以降、次世代ファイアウォール(Next-Generation Firewall)が登場しました:
5.2 OSI参照モデルとパケット処理
OSI 7層モデル
1984年、ISOはOSI参照モデル(Open Systems Interconnection Reference Model)を標準化しました。ネットワーク通信を7つの抽象層に分割します:
Layer 7: アプリケーション層 │ HTTP, FTP, SSH, DNS
Layer 6: プレゼンテーション層 │ SSL/TLS, 暗号化, 圧縮
Layer 5: セッション層 │ セッション管理, RPC
Layer 4: トランスポート層 │ TCP, UDP, ポート番号
Layer 3: ネットワーク層 │ IP, ICMP, ルーティング
Layer 2: データリンク層 │ Ethernet, MAC アドレス
Layer 1: 物理層 │ 電気信号, ケーブル
パケットのカプセル化
データは各層でカプセル化されます:
アプリケーションデータ
↓ [Layer 7]
┌─────────────────────────────────────┐
│ HTTP Request │
└─────────────────────────────────────┘
↓ [Layer 4: TCP]
┌─────────┬───────────────────────────┐
│TCP Header│ HTTP Request │
│ SRC: 54321 │
│ DST: 80 │ │
└─────────┴───────────────────────────┘
↓ [Layer 3: IP]
┌─────────┬─────────┬─────────────────┐
│IP Header│TCP Header│ HTTP Request │
│SRC: 192.168.1.100 │
│DST: 93.184.216.34 │
└─────────┴─────────┴─────────────────┘
↓ [Layer 2: Ethernet]
┌─────────────┬─────────┬─────────┬───┐
│Ethernet Hdr │IP Header│TCP Hdr │...│
│MAC addresses│ │ │ │
└─────────────┴─────────┴─────────┴───┘
ファイアウォールの動作層
パケットフィルタ(第一世代):
Layer 3-4: IP アドレス、ポート番号
ステートフルファイアウォール(第二世代):
Layer 3-4 + 接続状態追跡
アプリケーションファイアウォール(第三世代):
Layer 3-7: プロトコル内容まで検査
UFW/iptables:
主に Layer 3-4、一部 Layer 7
5.3 Linuxカーネルのnetfilterアーキテクチャ
netfilterフレームワーク
netfilterは、Linuxカーネル2.4(2001年)で導入されたパケットフィルタリングフレームワークです。Paul "Rusty" Russellが開発しました。
┌─────────────────────────────────────────────────────────┐
│ Linuxカーネル空間 │
│ ┌────────────────────────────────────────────────┐ │
│ │ netfilter フレームワーク │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ filter │ │ nat │ │ mangle │ │ │
│ │ │ テーブル│ │テーブル │ │テーブル │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↑
Netlink ソケット
↓
┌─────────────────────────────────────────────────────────┐
│ ユーザー空間 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ iptables │ │ nftables │ │ UFW │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
netfilterのフック(Hook)
パケットがカーネルを通過する際、5つのフックポイントでnetfilterが介入できます:
┌─────────────────┐
入力パケット ────────→│ PREROUTING │
└────────┬────────┘
↓
┌────────────────┐
│ ルーティング │
│ 決定 │
└───────┬────────┘
│
┌────────────────┴────────────────┐
↓ ↓
┌─────────────────┐ ┌─────────────────┐
│ INPUT │ │ FORWARD │
│ (ローカル宛) │ │ (転送トラフィック)│
└────────┬────────┘ └────────┬────────┘
↓ │
┌─────────────────┐ │
│ ローカルプロセス│ │
└────────┬────────┘ │
↓ │
┌─────────────────┐ │
│ OUTPUT │ │
│ (ローカル発信)│ │
└────────┬────────┘ │
↓ ↓
└────────────────┬────────────────┘
↓
┌─────────────────┐
│ POSTROUTING │
└────────┬────────┘
↓
出力パケット
テーブルとチェーン
netfilterは複数のテーブルを使用し、各テーブルにはチェーンが含まれます:
filterテーブル(デフォルト):
用途: パケットのフィルタリング(許可/拒否)
チェーン:
- INPUT: ローカルプロセス宛のパケット
- FORWARD: 転送されるパケット
- OUTPUT: ローカルプロセスが生成したパケット
natテーブル:
用途: ネットワークアドレス変換
チェーン:
- PREROUTING: 宛先NAT(DNAT)
- OUTPUT: ローカル生成パケットのDNAT
- POSTROUTING: 送信元NAT(SNAT/マスカレード)
mangleテーブル:
用途: パケットヘッダの変更(TOS, TTLなど)
チェーン: すべてのフックポイント
接続追跡(conntrack)
conntrackモジュールは、ステートフルインスペクションを実現します:
接続状態:
NEW: 新しい接続の最初のパケット
ESTABLISHED: 確立された接続に属するパケット
RELATED: 関連する接続(FTPデータ接続など)
INVALID: 状態を識別できないパケット
例: TCPハンドシェイク
SYN → 状態: NEW
← SYN-ACK 状態: ESTABLISHED
ACK → 状態: ESTABLISHED
(以降のパケット) 状態: ESTABLISHED
接続追跡により、応答パケットを自動的に許可できます:
# iptablesでの接続追跡ルール
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
5.4 iptablesからUFWへ
iptablesの複雑さ
iptablesは強力ですが、構文が複雑です:
# SSHポート22を許可(IPv4)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT
# IPv6も必要
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT
# デフォルトポリシー
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# ループバック許可
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# ICMP(ping)許可
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
UFW: 抽象化による簡素化
UFW(Uncomplicated Firewall)は、2008年にUbuntuプロジェクトで開発されました。iptablesの複雑さを隠蔽し、シンプルなインターフェースを提供します:
# 上記のiptablesルールをUFWで書くと:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
# たった4コマンドで同等の設定が完了
UFWのアーキテクチャ
┌─────────────────────────────────────────────┐
│ ユーザーコマンド │
│ sudo ufw allow 4242/tcp │
└────────────────────┬────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ UFW(Pythonスクリプト) │
│ /usr/sbin/ufw │
│ /usr/share/ufw/ │
└────────────────────┬────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ UFW設定ファイル │
│ /etc/ufw/user.rules │
│ /etc/ufw/user6.rules(IPv6) │
│ /etc/ufw/before.rules │
│ /etc/ufw/after.rules │
└────────────────────┬────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ iptables / ip6tables │
│ ルールに変換 │
└────────────────────┬────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ netfilter(カーネル) │
└─────────────────────────────────────────────┘
5.5 多層防御(Defense in Depth)
多層防御の理論
多層防御(Defense in Depth)は、軍事戦略から借用されたセキュリティ概念です。単一の防御層に依存するのではなく、複数の独立した防御層を重ねます。
攻撃者の視点:
┌─────────────────────────────────────────────────────┐
│ 外部ネットワーク │
└───────────────────────┬─────────────────────────────┘
↓ Layer 1: 境界ファイアウォール
┌─────────────────────────────────────────────────────┐
│ DMZ │
└───────────────────────┬─────────────────────────────┘
↓ Layer 2: 内部ファイアウォール
┌─────────────────────────────────────────────────────┐
│ 内部ネットワーク │
└───────────────────────┬─────────────────────────────┘
↓ Layer 3: ホストファイアウォール(UFW)
┌─────────────────────────────────────────────────────┐
│ ホストマシン │
└───────────────────────┬─────────────────────────────┘
↓ Layer 4: アプリケーションセキュリティ
┌─────────────────────────────────────────────────────┐
│ SSH設定 │
│ (ポート変更、鍵認証、etc.) │
└───────────────────────┬─────────────────────────────┘
↓ Layer 5: アクセス制御
┌─────────────────────────────────────────────────────┐
│ sudo設定、パスワードポリシー │
└─────────────────────────────────────────────────────┘
各層を突破するには、その層の防御を破る必要がある
→ 攻撃コストが累積的に増加
Born2berootでの多層防御
Layer 1: ファイアウォール(UFW)
- ポート4242のみ開放
- その他すべてブロック
Layer 2: SSHセキュリティ
- 非標準ポート(4242)
- rootログイン禁止
- 認証試行回数制限
Layer 3: アクセス制御
- sudo設定
- パスワードポリシー
- 監査ログ
Layer 4: システム強化
- パスワード複雑性要件
- サービス最小化
- 定期的な監視
最小権限の原則の適用
ファイアウォール設定は最小権限の原則に従います:
悪い設計:
デフォルト: ALLOW
明示的に危険なポートをブロック
→ 新しいサービスを忘れる可能性
→ 未知の脆弱性にさらされる
良い設計(Born2beroot):
デフォルト: DENY
必要なポートのみ明示的に許可
→ 最小限のアタックサーフェス
→ 忘れても安全側に倒れる
---
5.6 Born2berootのUFW設定
理論的基盤を理解したところで、Born2berootプロジェクトのUFW設定を実装します。
UFWのインストール
# パッケージリストを更新
sudo apt update
# UFWをインストール
sudo apt install ufw -y
# インストール確認
dpkg -l | grep ufw
# バージョン確認
sudo ufw version
デフォルトポリシーの設定
第5.5節で説明した最小権限の原則に基づき、デフォルトポリシーを設定します:
# 入力をデフォルトで拒否(セキュア)
sudo ufw default deny incoming
# 出力をデフォルトで許可(インターネット使用に必要)
sudo ufw default allow outgoing
# 設定確認
sudo ufw status verbose
SSH(ポート4242)の許可
Born2berootでは、ポート4242のみを開放します:
# ポート4242/TCPを許可
sudo ufw allow 4242/tcp
# コメント付きで追加(推奨)
sudo ufw allow 4242/tcp comment 'SSH Born2beroot'
# 確認
sudo ufw status numbered
理論的根拠:
- 第4章で設定したSSHはポート4242を使用
- その他のポートは不要なためブロック
UFWの有効化
# UFWを有効化
sudo ufw enable
# 警告メッセージ:
# Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
# Firewall is active and enabled on system startup
# 状態確認
sudo ufw status verbose
# 出力:
# Status: active
# Logging: on (low)
# Default: deny (incoming), allow (outgoing), disabled (routed)
#
# To Action From
# -- ------ ----
# 4242/tcp ALLOW IN Anywhere
# 4242/tcp (v6) ALLOW IN Anywhere (v6)
システム起動時の自動有効化
# UFWをシステム起動時に自動有効化
sudo systemctl enable ufw
# 確認
sudo systemctl is-enabled ufw
5.7 UFWルール管理
ルールの追加と削除
# ポートを許可
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
# ポート範囲を許可
sudo ufw allow 6000:6007/tcp
# 特定のIPアドレスからの接続を許可
sudo ufw allow from 192.168.1.100 to any port 4242
# ルールを番号付きで表示
sudo ufw status numbered
# 番号でルールを削除
sudo ufw delete 2
# ルールを直接指定して削除
sudo ufw delete allow 80/tcp
ルールの優先順位
UFWルールは上から順に評価されます。最初にマッチしたルールが適用されます:
# 例: IPアドレス192.168.1.100からのSSHをブロック、他は許可
sudo ufw deny from 192.168.1.100 to any port 4242
sudo ufw allow 4242/tcp
# ルールの順序を確認
sudo ufw status numbered
# [ 1] 4242/tcp DENY IN 192.168.1.100
# [ 2] 4242/tcp ALLOW IN Anywhere
# 192.168.1.100からはルール1でブロック
# 他のIPからはルール2で許可
ログ記録
# ログ記録を有効化
sudo ufw logging on
# ログレベル設定
sudo ufw logging low # 推奨
sudo ufw logging medium
sudo ufw logging high # 詳細(ディスク使用量注意)
# ログの確認
sudo tail -f /var/log/ufw.log
# ブロックされた接続を確認
sudo grep "UFW BLOCK" /var/log/ufw.log | tail -10
ログ形式の解読:
Dec 7 15:00:01 hostname kernel: [UFW BLOCK] IN=enp0s3 OUT=
MAC=... SRC=192.168.1.100 DST=10.0.2.15 LEN=60 TOS=0x00
PREC=0x00 TTL=64 ID=12345 PROTO=TCP SPT=54321 DPT=80
WINDOW=65535 RES=0x00 SYN URGP=0
解読:
[UFW BLOCK]: パケットがブロックされた
IN=enp0s3: 入力インターフェース
SRC=192.168.1.100: 送信元IP
DST=10.0.2.15: 宛先IP
PROTO=TCP: プロトコル
SPT=54321: 送信元ポート
DPT=80: 宛先ポート(HTTPがブロックされた)
5.8 ネットワーク診断
ポートの確認
# リスニングしているポートを表示
sudo ss -tlnp
# または
sudo netstat -tlnp
# 出力例:
# State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
# LISTEN 0 128 0.0.0.0:4242 0.0.0.0:* users:(("sshd",pid=612,fd=3))
# オプションの意味:
# -t: TCP
# -l: リスニングソケット
# -n: 数値で表示(名前解決しない)
# -p: プロセス情報
接続テスト
# ポートへの接続テスト(netcat)
nc -zv localhost 4242
# 出力: Connection to localhost 4242 port [tcp/*] succeeded!
nc -zv localhost 22
# 出力: nc: connect to localhost port 22 (tcp) failed: Connection refused
# ping(ICMP)
ping -c 4 8.8.8.8
# 経路追跡
traceroute google.com
# DNS解決
nslookup google.com
dig google.com
ファイアウォールルールの確認
# UFWの状態(簡潔)
sudo ufw status
# UFWの状態(詳細)
sudo ufw status verbose
# 番号付き(削除用)
sudo ufw status numbered
# 生のiptablesルールを表示
sudo iptables -L -n -v
sudo iptables -L -n -v -t nat
5.9 実践演習
シナリオ1: 新しいポートの開放と閉鎖
# 1. 現在のルールを確認
sudo ufw status numbered
# 2. ポート8080を開放
sudo ufw allow 8080/tcp
# 3. 確認
sudo ufw status
# 4. ポート8080を閉鎖
sudo ufw delete allow 8080/tcp
# 5. 確認
sudo ufw status
シナリオ2: ファイアウォールのテスト
# 1. SSH接続テスト(許可されているはず)
ssh username@localhost -p 4242
# 2. 別のポートへの接続テスト(拒否されるはず)
nc -zv localhost 22
# 出力: Connection refused
# 3. UFWログでブロックを確認
sudo grep "UFW BLOCK" /var/log/ufw.log | tail -5
シナリオ3: ルールのリセット
# すべてのルールを削除してデフォルトに戻す
sudo ufw reset
# 警告: すべてのルールが削除される
# 必要なルールを再度追加
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 4242/tcp
sudo ufw enable
5.10 トラブルシューティング
UFW有効化後にSSH接続が切れる
原因: SSHポートを開放する前にUFWを有効化した
解決:
# 仮想マシンのコンソールから直接ログイン
# (VirtualBoxウィンドウで操作)
# UFWを無効化
sudo ufw disable
# SSHポートを開放
sudo ufw allow 4242/tcp
# UFWを再有効化
sudo ufw enable
ルールを追加しても接続できない
解決:
# UFWをリロード
sudo ufw reload
# SSHサービスが起動しているか確認
sudo systemctl status ssh
# ポートがリスニングしているか確認
sudo ss -tlnp | grep 4242
# ルールの順序を確認(denyルールが先にないか)
sudo ufw status numbered
5.11 理論と実践の統合
この章で学んだ概念の関係:
歴史的発展
├── パケットフィルタ(1988)→ ステートレス検査
├── ステートフルインスペクション(1994)→ 接続追跡
└── アプリケーションファイアウォール → Layer 7検査
Linuxアーキテクチャ
├── netfilter(カーネル)
│ ├── フック: PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING
│ ├── テーブル: filter, nat, mangle
│ └── conntrack: 接続状態追跡
├── iptables(ユーザー空間)
└── UFW(抽象化層)
セキュリティ原則
├── 最小権限 → デフォルト拒否
├── 多層防御 → 複数の防御層
└── フェイルセーフ → 忘れても安全側
Born2beroot実装
├── default deny incoming
├── default allow outgoing
├── allow 4242/tcp(SSHのみ)
└── logging on
5.12 次章への準備
次章(最終章)ではパスワードポリシーと監視スクリプトを学びます:
- パスワードエントロピーの理論
- PAMモジュールによるポリシー強制
- シェルスクリプトによるシステム監視
- cronによるタスクスケジューリング
次章に進む前に、スナップショットを作成してください:
VBoxManage snapshot "Born2beroot" take "After Firewall Config" \
--description "UFW configured, port 4242 only"