Webシェルという「見えないナイフ」を封じ込める:FIMと防御の最前線
システム運用現場で最も背筋が凍る瞬間を知っているか? それは、ログを眺めている最中に、見覚えのない .php や .jsp ファイルが公開ディレクトリにポツンと生成されているのを見つけた時だ。
攻撃者は、ファイルアップロード機能の脆弱性や、RCE(リモートコード実行)を悪用して、彼らの「足場」となるWebシェルを設置する。一度設置されれば、あとはコマンドライン一つでサーバーを完全に掌握される。教科書的な「権限分離をしましょう」という言葉は、戦場では無力だ。今日は、この「見えないナイフ」を検知し、無力化するための泥臭い実践論を語る。
—
1. 攻撃者が狙う盲点:なぜ「静的な防御」だけでは抜かれるのか
多くの開発者は「アップロード時の拡張子チェック」や「保存先の非公開化」で安心する。だが、攻撃者はその先を行く。
- MIMEタイプの偽装:
Content-Typeをimage/jpegに書き換えるのは基本中の基本だ。 - バイナリ埋め込み: 画像ファイルの中にPHPコードを埋め込み、Webサーバーの設定ミス(
AddHandlerの悪用など)を突いて実行させる。 - ファイル名難読化:
shell.phpではなく、パーミッションやタイムスタンプを細工したファイルを、既存のライブラリフォルダに紛れ込ませる。
これらを防ぐには、「ファイルはいつか必ず突破される」という前提に立ち、システムが「異変」を即座に感知する仕組み(FIM: File Integrity Monitoring)が不可欠だ。
—
2. 実践的検知:Auditdによるリアルタイム監視
OSSECも良いツールだが、導入のオーバーヘッドが大きい。もっと軽量かつ確実に「誰が・いつ・どのファイルを生成したか」を追うなら、Linuxの auditd に勝るものはない。
以下の設定を /etc/audit/rules.d/audit.rules に追加し、Web公開ディレクトリ(例: /var/www/html/uploads)を監視せよ。
# /var/www/html/uploads ディレクトリへの書き込みを監視
# -p wa: 書き込み(w)と属性変更(a)をトリガー
# -k web_shell_alert: ログ検索用のキーラベル
-w /var/www/html/uploads/ -p wa -k web_shell_alert
設定後、auditctl -R /etc/audit/rules.d/audit.rules で反映させる。これで、このディレクトリに何かが書き込まれるたびに、/var/log/audit/audit.log に詳細なプロセス情報が刻まれる。
—
3. 防御の要:アップロード処理のセキュア実装(PHP)
ファイルアップロード処理を書く際、多くのエンジニアが犯すミスは「拡張子のみで判断すること」だ。以下のサンプルは、最低限守るべき「守りの基本」を詰め込んだものだ。
<?php
// アップロード先は公開領域から分離し、実行権限のない場所に置くことが大前提
$upload_dir = '/var/www/data/uploads/';
$filename = basename($_FILES['userfile']['name']);
// 1. ファイルサイズ制限
if ($_FILES['userfile']['size'] > 1024 * 1024) {
die("ファイルサイズが大きすぎます");
}
// 2. MIMEタイプをサーバー側で厳格検証
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['userfile']['tmp_name']);
$allowed_types = ['image/jpeg', 'image/png'];
if (!in_array($mime, $allowed_types)) {
die("不正なファイル形式です");
}
// 3. ファイル名をハッシュ化してランダム化(ディレクトリトラバーサル対策)
$new_filename = bin2hex(random_bytes(16)) . '.img';
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $upload_dir . $new_filename)) {
echo "アップロード成功";
} else {
echo "アップロード失敗";
}
?>
—
4. Nginxで「実行」を物理的に遮断する
Webシェルが置かれたとしても、Webサーバーがそれを「実行」しなければ無害だ。Nginxの設定で、アップロードディレクトリ内でのPHP実行を完全に禁止する。これが最も強力な防波堤となる。
# アップロードディレクトリへのアクセス設定
location /uploads/ {
# このディレクトリ内ではPHPなどのスクリプトを一切実行させない
location ~ \.php$ {
return 403;
}
# 画像ファイルのみ配信を許可
types { } default_type application/octet-stream;
# 隠しファイル(.htaccess等)へのアクセスを拒否
location ~ /\. {
deny all;
}
}
—
5. 最後に:現場のエンジニアへ送るアドバイス
FIMの構築やアップロード制限は、「面倒な作業」に見えるかもしれない。しかし、インシデントが発生した際、被害の拡大を食い止め、原因を特定するための「唯一の証拠」になるのが、今回紹介したログや設定だ。
- 自動化せよ:
auditdのログをSlackやPagerDutyに飛ばす小さなスクリプトを組むだけで、初動対応のスピードは段違いになる。 - 疑え: どんなに厳重なチェックを通したファイルでも、ファイルシステム上で見知らぬファイルが増えていないか、週に一度は
find /var/www/html/uploads -mtime -1で確認する癖をつけよう。
セキュリティとは、完璧な製品を買うことではなく、「何が起きているかを常に把握し続ける泥臭いプロセス」そのものだ。今日紹介した手法を、今すぐ君のサーバーで試してほしい。それが、明日、君が安眠するための投資になる。
コメント