境界防御の幻想を打ち砕く:ゼロトラスト時代におけるマイクロセグメンテーションの実践とラテラルムーブメント封鎖
多くの企業が「クラウド移行」を成し遂げた後、同じ過ちを繰り返している。オンプレミス時代に築き上げた「堅牢な城壁と、一度中に入ればフリーパスの内部」という境界型セキュリティモデルを、そのままVPC(Virtual Private Cloud)の中に持ち込んでいるのだ。
「VPCに入ってしまえば、あとはどのインスタンス間でも通信し放題」——この設計思想こそが、近年のランサムウェアやAPT攻撃において、初期侵入を許した瞬間に組織全体が壊滅する根本原因である。攻撃者は、一度踏み台となる脆弱なコンテナやWebサーバーのシェルを奪取(RCE)すると、内部ネットワークを自在に徘徊し、ドメインコントローラーやデータベースへと到達する。これがラテラルムーブメント(横展開)の悪夢だ。
本稿では、ネットワークセグメンテーションの基本から、クラウドネイティブ環境におけるマイクロセグメンテーションによる「絶対に破らせない防衛線」の構築手法を、現場の泥臭いインシデントハンドリングの知見を交えて徹底解説する。
—
1. 境界防御の限界と、ネットワークセグメンテーションの再定義
従来のVPC設計では、パブリックサブネットとプライベートサブネットという大雑把な分割にとどまりがちだ。しかし、これだけでは不十分である。例えば、同一のプライベートサブネット内に配置された「社内向けAPIサーバー」と「実験用の脆弱なコンテナ」が、お互いに自由に通信できる状態であれば、前者は常に人質にとられているようなものだ。
真のセキュリティアーキテクチャにおいては、ネットワークセグメンテーションを「トラフィックの隔離」ではなく「アイデンティティに基づく最小特権の強制」として再定義しなければならない。
従来のVPC設計の盲点
- VPCピアリングやTransit Gatewayの安易な接続: 異なるVPC間を全開放することで、一箇所の脆弱性が全システムへのバックドアと化す。
- CIDRブロック単位のラフな制御: サブネットという「面」での防御は、IPアドレスの偽装やARPスプーフィング(クラウド内ではないが、コンテナネットワーク空間でのレイヤ2の誤認)に対して無力である。
これに対抗する唯一の解が、マイクロセグメンテーション、すなわちホスト単位、さらにはプロセス単位での通信制御である。
—
2. セキュリティグループ(SG)とネットワークACL(NACL)の正しい使い分け
クラウドインフラストラクチャー(AWSを例に取る)において、通信制御の要となるのは Security Group (SG) と Network ACL (NACL) だ。しかし、現場のレビューを行っていると、この2つのレイヤの特性を混同した悲惨な設定に度々遭遇する。
| 特性 | セキュリティグループ (SG) | ネットワークACL (NACL) |
| :— | :— | :— |
| 適用レイヤ | ステートフル(戻りの通信を自動許可) | ステートレス(往復のルールを個別に記述) |
| 適用対象 | インスタンス(ENI)単位 | サブネット単位 |
| 評価順序 | すべてのルールを評価してから判定 | ルールの番号順(上から順に評価) |
ラテラルムーブメントを確実に阻止するためには、NACLはサブネット境界での大まかなフィルタリング(DDoS対策や外部との明確な遮断)に留め、インスタンス間の厳密な通信制御はすべてステートフルなセキュリティグループで行うべきだ。
陥りがちな罠:セキュリティグループの「自己参照(Self-Referencing)」
よくあるアンチパターンは、同一セキュリティグループ内の全通信を 0.0.0.0/0 やサブネットCIDRで許可してしまうことだ。「同じグループだから大丈夫だろう」という油断が、侵害された1台を踏み台にした同階層内でのラテラルムーブメントを許容してしまう。
正しいアプローチは、セキュリティグループID自体を送信元(Source)に指定する「自己参照ルール」の適用である。
—
3. 実践:IaC(Terraform)によるマイクロセグメンテーションの実装
机上の空論を語っても始まらない。ここでは、Terraformを用いて、Web層、アプリケーション層、データベース層の間で、厳密なマイクロセグメンテーションを強制するコード例を示す。
攻撃者がWeb層のインスタンスを完全に乗っ取ったとしても、DB層への通信は特定のポート・特定のサービスからしか到達できないよう、多重にロックダウンする。
# ==========================================
# 1. データベース層のセキュリティグループ
# ==========================================
resource "aws_security_group" "db_tier" {
name ="sg-production-db-tier"
description ="Database tier security group with strict micro-segmentation"
vpc_id = var.vpc_id
# 【重要】App層からの特定のトラフィック(PostgreSQL: 5432)のみを許可
# ネットワーク全体の全開放を避け、App層のSG IDを明示的に指定する
ingress {
description = "Allow inbound PostgreSQL traffic strictly from Application Tier"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.app_tier.id]
}
# アウトバウンドは原則としてゼロトラスト(必要最小限の外部通信のみ許可、または完全閉塞)
# パッチ適用やログ転送に必要な場合を除き、外部への直接通信は一切禁止する
egress {
description = "Block all outbound traffic by default (Zero Trust egress)"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"] # ※実運用ではNAT Gateway経由のホワイトリスト化を推奨
}
tags = {
Name = "prod-db-sg"
Environment = "Production"
}
}
# ==========================================
# 2. アプリケーション層のセキュリティグループ
# ==========================================
resource "aws_security_group" "app_tier" {
name ="sg-production-app-tier"
description ="Application tier security group"
vpc_id = var.vpc_id
# 負荷分散(ALB)からのトラフィックのみを許可
ingress {
description = "Allow HTTP/HTTPS traffic strictly from ALB"
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
# 【自己参照の禁止】App層同士の不要な通信をブロックし、必要なピア間通信のみ定義
ingress {
description = "Allow internal cluster communication only for specific microservices"
from_port = 8080
to_port = 8080
protocol = "tcp"
self = true # 同一SG間での通信が必要な場合のみ。不要なら削除すること。
}
egress {
description = "Allow app to reach DB tier securely"
from_port = 5432
to_port = 5432
protocol = "tcp"
# 送先をDB層のSGに完全固定することで、他のリソースへの不正なスキャンを防ぐ
security_groups = [aws_security_group.db_tier.id]
}
tags = {
Name = "prod-app-sg"
Environment = "Production"
}
}
# ==========================================
# 3. ALB(ロードバランサー)のセキュリティグループ
# ==========================================
resource "aws_security_group" "alb" {
name ="sg-production-alb"
description ="ALB security group facing the public internet"
vpc_id = var.vpc_id
# パブリックからのHTTPSアクセスを許可
ingress {
description = "Allow inbound HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# アプリケーション層への通信のみを許可
egress {
description = "Allow traffic to Application Tier"
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [aws_security_group.app_tier.id]
}
tags = {
Name = "prod-alb-sg"
Environment = "Production"
}
}
このTerraformコードのポイントは、「どのリソースから、どのリソースへ、どのレイヤのプロトコルで通信するか」がコード上ですべて明文化されている点にある。これにより、後から人が手動でAWSマネジメントコンソールをいじり、セキュリティホールを開けてしまう「コンソール運用の呪縛」を防ぐことができる。
—
4. 監査と侵入テスト(Red Team的視点)からの考察
セキュリティアーキテクトとして、我々は「設定したから安全だ」という性善説に立ってはいけない。構築したマイクロセグメンテーションが実際に機能しているかを検証するための監査ポイントを挙げる。
1. ポートスキャンによるラテラルムーブメントのシミュレーション
侵入テスト(ペネトレーションテスト)において、Red Teamは侵害されたインスタンスの内部から nmap や masscan を実行し、周囲のホストの生存確認と開放ポートの列挙を試みる。
- 合格ライン: セキュリティグループが正しく機能していれば、許可されていない宛先へのパケットはすべてドロップ(またはタイムアウト)となり、攻撃者は「生きていないホスト(あるいは存在しないネットワーク)」と誤認する。
2. コンテナ環境(Kubernetes / eBPF)への拡張
VPCレベルのセグメンテーションに加え、Kubernetesクラスター内を運用している場合は、Kubernetes NetworkPolicies や、より高度な eBPF(Extended Berkeley Packet Filter)ベースのセキュリティツール(Cilium等)を用いたレイヤ7(HTTPパス単位)のマイクロセグメンテーションの導入が不可欠だ。
L4(IP/ポート)レベルの制御だけでは、同じポートで動く異なるマイクロサービス間(例:正当なAPIと、脆弱性のある管理画面)の不正なアクセスを防ぎきれないからだ。
—
結び:セキュリティは「構造」で勝て
セキュリティインシデントのニュースを見るたび、私は「人間を信頼したシステムは必ず裏切られる」という現実を痛感する。開発者が急ぎで穴を開けたファイアウォールルール、運用者が一時的にと称して開放した 0.0.0.0/0。これらはすべて、攻撃者にとっての「ご招待状」である。
マイクロセグメンテーションの本質は、システムのどこかに脆弱性が潜んでいて「侵入されること」を前提にし、「侵入された後に、いかに被害をその部屋一つに閉じ込めるか(Blast Radiusの最小化)」という冷徹なエンジニアリングにある。
境界防御の幻想を捨て、VPCの隅々までゼロトラストの網の目を張り巡らせること。それこそが、現代のセキュリティアーキテクトに課された最大の責務である。
コメント