なぜ「/tmp」を封鎖するのか?:現場が教えるLinux要塞化の鉄則
現場でインシデント対応をしていると、侵入の足跡を辿る過程で必ずと言っていいほど目にする光景がある。それは、Webサーバーの /tmp ディレクトリに設置された、見覚えのないシェルスクリプトやバイナリの残骸だ。
「なぜこんなところに?」と若手エンジニアに問うと、多くの者が「そこが一時的な作業場所だから」と答える。だが、攻撃者にとって /tmp は、システムの脆弱性を突いた後に「自分の武器(ペイロード)」を置き、実行するための格好の隠れ家だ。
今日は、Linuxサーバーの堅牢化において「呼吸をするように当たり前」に行うべき、/tmp および /var/tmp の noexec マウントについて、実務的な観点から解説しよう。
—
1. 攻撃者が /tmp を狙う理由(PoC的視点)
攻撃者がWebアプリケーションの脆弱性(ファイルアップロードの不備や、コマンドインジェクション)を突いて侵入した際、彼らがまず行うのは「自分たちが実行可能な環境の確保」だ。
もしサーバーが攻撃者の書き込みを許し、かつその場所でスクリプトの実行を許可していれば、以下のようなフローが成立してしまう。
1. アップロード: 脆弱性を突いてバックドア(shell.php や backdoor.sh)を /tmp に送り込む。
2. 権限付与: chmod +x /tmp/backdoor.sh を実行。
3. 実行: /tmp/backdoor.sh を叩いてリバースシェルを確立。
この時、もし /tmp が noexec でマウントされていれば、手順3の実行段階で「Permission denied(実行権限がありません)」と弾かれる。たったこれだけの防御策が、侵入後の被害拡大を劇的に抑え込むのだ。
—
2. 実践:/tmp を堅牢化する設定
実務環境では、/etc/fstab を編集してマウントオプションを適用するのが定石だ。以下に、安全かつ確実な設定手順を示す。
/etc/fstab の設定例
既存のパーティション設定に noexec, nosuid, nodev を追加する。これらは「三種の神器」とも言える重要なオプションだ。
# /etc/fstab への追記例
# tmpfsとしてメモリ上に配置するとさらに高速かつ安全になる
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,size=1G 0 0
tmpfs /var/tmp tmpfs defaults,noexec,nosuid,nodev,size=1G 0 0
noexec: そのパーティション内でのバイナリ実行を禁止。nosuid:set-user-identifierビットを無視(権限昇格を防ぐ)。nodev: デバイスファイル(キャラクタデバイスやブロックデバイス)の解釈を禁止。
設定後は mount -o remount /tmp で適用する。ただし、システム全体に適用する前に、必ず検証環境でアプリケーションが動作するか確認すること。 たまに、ログローテートや一時ファイル作成で実行ファイルを必要とする行儀の悪いミドルウェアが存在するためだ。
—
3. アプリ開発者が知るべき「安全な一時ファイルの扱い」
インフラ側で noexec を適用したとしても、コードレベルで脆弱な書き方をしていては意味がない。特にPHPやPythonで一時ファイルを扱う際は、標準ライブラリの安全な関数を使うことが鉄則だ。
Pythonによる安全な一時ファイル生成(例)
Pythonの tempfile モジュールを使えば、権限設定を適切に行った上で安全に一時ファイルを生成できる。
import tempfile
import os
# 安全な一時ファイルの作成
# delete=Trueにすれば、終了時に自動削除される
with tempfile.NamedTemporaryFile(mode='w+', delete=True) as tmp:
tmp.write("安全なデータ処理")
tmp.seek(0)
# ここで処理を行う
print(f"作成された一時ファイル: {tmp.name}")
# もし手動で作成する場合も、パーミッションを厳格に制御する
# os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
PHPにおける脆弱なコードの回避
PHPでファイルをアップロードする際、move_uploaded_file() を使わずに、直接 /tmp に書き込んで実行させるような実装は論外だ。
// 悪い例:ユーザー入力をそのまま実行可能なパスに配置
// move_uploaded_file($_FILES['file']['tmp_name'], '/tmp/' . $_FILES['file']['name']);
// 良い例:アップロード先をnoexecかつ公開ディレクトリ外に指定し、拡張子を検証する
$uploadDir = '/var/www/uploads/'; // 公開領域からはアクセス不可
$filename = basename($_FILES['file']['name']);
// ここで拡張子チェックを厳格に行う
if (in_array(pathinfo($filename, PATHINFO_EXTENSION), ['jpg', 'png'])) {
move_uploaded_file($_FILES['file']['tmp_name'], $uploadDir . $filename);
}
—
最後に:防御は「多層」で考える
「/tmp を noexec にしたから完璧だ」とは決して言わないでほしい。これはあくまで「侵入された後の被害」を最小化するための、数ある防波堤の一つに過ぎない。
セキュリティの本質は、「攻撃者がどれほど巧妙に動いても、どこかで必ず躓く罠を張り巡らせておくこと」だ。
今回の設定は、インフラエンジニアの運用負荷をほとんど上げることなく、セキュリティレベルを一段引き上げる非常に費用対効果の高い施策だ。今日、サーバーにログインしたら、まずは mount コマンドを叩いて、自分の管轄しているサーバーの /tmp がどうなっているか確認するところから始めてみてほしい。
現場からは以上だ。次は「ログの改ざん検知」について深掘りしようか。準備ができたらまた声をかけてくれ。
コメント