攻撃者の「足がかり」を断つ:systemd要塞化による攻撃対象領域の極小化
現場でインシデント対応をしていると、いつも痛感することがある。「なぜ、このWebサーバーで avahi-daemon が動いているんだ?」と。
攻撃者は、あなたのアプリケーションの脆弱性(RCEやSQLi)を突く前に、まずその「足元の脆弱性」をスキャンしている。不要なサービスは、攻撃者にとっての「贈り物」だ。たとえWebアプリが堅牢でも、裏で動いている設定不備のデーモンが権限昇格の踏み台になれば、ゲームオーバーは一瞬だ。
今日は、systemdを駆使して「攻撃者が入り込む隙間すら残さない」ための、泥臭くも強力な要塞化術を伝授する。
—
1. なぜ systemctl disable では足りないのか
多くのエンジニアが systemctl disable で満足している。だが、これは「自動起動をオフにする」だけで、別のサービスからの依存関係で勝手に起動したり、手動で誤って実行されるリスクが残る。
我々がやるべきは、systemctl mask による「恒久的な無効化」だ。これは対象のユニットファイルを /dev/null へのシンボリックリンクに置き換える。つまり、何があってもそのサービスは起動不能になる。物理的な「封印」に近い。
攻撃者の視点:なぜそこを狙うのか
攻撃者は、rpcbind や cups、あるいは古い telnet のような、メモリ破壊の脆弱性が放置されがちなサービスを常に探している。これらが動いていれば、たとえWebサーバーが chroot や docker で隔離されていても、カーネルや他のプロセスへ横展開(Lateral Movement)する突破口にされる。
—
2. 実践:攻撃対象領域を最小化する構成
サーバー構築時の「最低限の構成」を自動化するスクリプトを紹介しよう。これをプロビジョニングの最終段階で流し込むだけで、サーバーの守りは格段に堅くなる。
セキュアなサービス遮断用スクリプト (hardening.sh)
#!/bin/bash
# セキュリティチーフの鉄則:不要なサービスは物理的に殺す
# このスクリプトは、一般的なWebサーバーで不要なサービスのリストをマスクする
SERVICES_TO_MASK=(
"avahi-daemon" # mDNSサービス。サーバーには不要
"cups" # プリンタサービス。Webサーバーで使う理由は皆無
"rpcbind" # RPCサービス。脆弱性の宝庫
"nfs-server" # NFSを直接Webサーバーで動かす必要はない
"telnet" # 論外。SSHがあれば即座に無効化
)
for service in "${SERVICES_TO_MASK[@]}"; do
if systemctl list-unit-files | grep -q "^$service"; then
echo "[-] サービス $service をマスクしています..."
systemctl stop "$service" 2>/dev/null
systemctl mask "$service"
fi
done
echo "[+] ハーデニング完了。攻撃対象領域を最小化しました。"
—
3. アプリケーション層での防衛:systemdとの連携
インフラ側でサービスを絞っても、アプリケーション側で「OSのコマンドを実行できる」ような書き方をしていれば無意味だ。例えば、PHPの shell_exec 等でサーバーの情報を覗こうとする攻撃は後を絶たない。
以下は、万が一アプリケーションが突破された際、攻撃者が環境を偵察できないようにするためのPHPの防衛的実装例だ。
不正なコマンド実行を抑止するサンプル (security_check.php)
<?php
/**
* 攻撃者が shell_exec 等でサーバー情報を探ろうとする際、
* 可能な限り情報を返さないための防衛的アプローチ。
*/
function secure_execute($command) {
// 許可されたコマンド以外は実行させないホワイトリスト
$allowed_commands = ['/usr/bin/git status', '/usr/bin/whoami'];
if (!in_array($command, $allowed_commands)) {
// 本来はログに記録してアラートを飛ばすべき
error_log("セキュリティ警告: 不正なコマンド試行 - " . $command);
return "Access Denied.";
}
return shell_exec($command);
}
// OSの全サービスリストを表示させようとする攻撃をブロック
$cmd = $_GET['cmd'] ?? '';
echo secure_execute($cmd);
?>
—
4. 最後に:エンジニアが持つべき「疑いの精神」
システム運用において、最も危険なのは「デフォルトで動いているから、必要なのだろう」という思考停止だ。
systemd の世界では、systemctl list-units --type=service --state=running を定期的に確認してほしい。そこに「なぜ動いているのか説明できないプロセス」があるなら、それが攻撃者の次の足がかりだ。
今日の宿題:
1. 今すぐ自分の管理するサーバーで systemctl list-unit-files --state=enabled を叩いてみろ。
2. そのリストの中に、Webアプリの機能に直結しないサービスがあれば、即座に要件を確認し、不要なら mask してしまえ。
セキュリティとは、派手なファイアウォール製品を導入することではない。泥臭く、不要な窓を一つずつ閉じていく作業の積み重ねだ。その地道な作業こそが、あなたのインフラを鉄壁にする。
コメント