コンテナ環境のフォレンジック:消えゆく「証拠」をどう掴むか
こんにちは。SOCで日々、攻撃者といたちごっこを繰り広げているチーフエンジニアです。
さて、最近の現場で耳にする「コンテナを使っているから、インシデントが起きてもコンテナを破棄して作り直せばクリーンになるよね」という言葉。これを聞くと、私は背筋が凍る思いがします。確かにコンテナは使い捨て可能ですが、それは「攻撃の痕跡を自らゴミ箱に捨てている」のと同じだからです。
メモリ上にしか存在しないファイルレスマルウェアや、実行中のインメモリプロセスを特定せずにコンテナを消すことは、犯人の足跡を証拠隠滅しているようなもの。今日は、コンテナ環境におけるフォレンジックの泥臭い現実と、それを防ぐための「守り」の技術を紐解いていきましょう。
—
1. なぜコンテナのフォレンジックは「悪夢」なのか
従来の物理サーバーやVMであれば、ディスクのイメージを取得してオフラインで解析すれば済みました。しかし、Kubernetes(K8s)環境では状況が異なります。
- 揮発性(Ephemeral): Podが再起動すれば、メモリの内容も一時ファイルもすべて消える。
- 共有カーネル: ホストOSのカーネルを共有しているため、コンテナ内のプロセスがホスト側に影響を及ぼしている場合、境界の特定が極めて困難。
- オーバーレイファイルシステム:
docker commitでイメージ化しても、実行時に動的に生成されたメモリ上の痕跡は拾えない。
攻撃者はここを突きます。彼らはコンテナのライフサイクルが短いことを逆手に取り、検知される前にタスクを終えてコンテナを消滅させる「Hit and Run」戦法をとるのです。
—
2. 現場で使える「死体検分」の事前準備
インシデント発生後に慌ててツールを入れても、攻撃者はログを消しています。事前の「観測体制」がすべてです。
チェックポイント:サイドカーによるログの外部転送
コンテナ内にログを留めず、サイドカーパターンでFluentdやVectorを配置し、ログを即座に外部のSIEM(SplunkやDatadogなど)へ飛ばしてください。
チェックポイント:メモリダンプとプロセスの取得
実行中のプロセスを保全するなら、kubectl debug を活用して、実行中のPodに「フォレンジック用コンテナ」をアタッチするのが王道です。
# 実行中のPodに対して、gdbやlsofを含むデバッグ用コンテナをアタッチする
kubectl debug -it <pod-name> --image=nicolaka/netshoot --target=<container-name>
—
3. Webアプリで防ぐべき「初期侵入」のPoCと実装
攻撃者がコンテナ内で活動を開始するトリガーの多くは、アプリケーションの脆弱性(RCE等)です。例えば、以下のPHPコードは、ファイルアップロード機能からWebシェルを仕込まれる典型的な脆弱性です。
脆弱な実装例(これを作ってはいけない)
<?php
// ユーザーがアップロードしたファイルをそのまま保存する極めて危険なコード
$target_path = "/var/www/html/uploads/" . $_FILES['file']['name'];
move_uploaded_file($_FILES['file']['tmp_name'], $target_path);
// この後、ブラウザから直接このPHPにアクセスされるとRCEが成立
?>
セキュアな実装例(防御の鉄則)
ファイルアップロードを許可する場合は、ファイル名をランダム化し、アップロード先をスクリプト実行不可のディレクトリにし、かつMIMEタイプを厳密にチェックします。
<?php
$upload_dir = '/var/www/uploads/';
// 1. ファイル名を乱数化し、拡張子を固定する
$safe_name = bin2hex(random_bytes(16)) . '.png';
$tmp_path = $_FILES['file']['tmp_name'];
// 2. MIMEタイプを検証(拡張子だけで判断しない)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $tmp_path);
if ($mime === 'image/png') {
move_uploaded_file($tmp_path, $upload_dir . $safe_name);
} else {
die("不正なファイル形式です");
}
// このディレクトリにはNginx側でphpの実行を禁止する設定を必ず入れること
?>
—
4. インフラ側での「徹底防御」設定
アプリケーション層だけでなく、インフラ側で「コンテナを牢獄にする」設定も不可欠です。
Nginxでのディレクトリ実行権限制限
特定のディレクトリでのPHP実行を禁止し、Webシェルの実行を封じます。
location /uploads/ {
# アップロードディレクトリではPHPスクリプトを一切実行させない
location ~ \.php$ {
deny all;
}
}
K8sのSecurityContext(最小権限の原則)
コンテナがルート権限で動くことは、フォレンジックの観点からも致命的です。必ず runAsNonRoot を設定してください。
# deployment.yamlの一部
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false # 昇権を禁止
readOnlyRootFilesystem: true # ルートファイルシステムを読み取り専用に
—
最後に:フォレンジックは「文化」である
インシデント対応の現場で最も悔しいのは、「ログがあれば犯人がわかったのに」という状況です。コンテナ環境におけるフォレンジックは、単なる技術の問題ではなく、「攻撃された時に何を残すべきか」を設計段階から組み込む文化そのものです。
まずは、今日紹介したような「読み取り専用のファイルシステム」や「ログの外部転送」から始めてみてください。それが、万が一の時にあなたの会社を守る最後の砦になります。
次回のブログでは、具体的に「コンテナからホストへ脱獄(Container Escape)された際、どこを確認すべきか」という、より深い深淵について掘り下げていきたいと思います。それでは、良いエンジニアリングを。
コメント