こんにちは。セキュリティの世界へようこそ。
「OSコマンドインジェクション」という言葉、教科書で目にすると何やら難しそうに見えますよね。でも、実はこれ、私たちの身近な「鍵の掛け方」と全く同じ理屈なんです。
今日は、なぜ攻撃者は「コマンド」を送り込んでくるのか、そして私たちがどうやってそれを防げばいいのか。専門用語を並べるのは一旦置いておいて、一緒に紐解いていきましょう。
—
1. 泥棒が「合鍵」を作る仕組み:OSコマンドインジェクションとは?
想像してみてください。あなたは自分の家に住んでいて、玄関に「荷物を置いておいて」と頼める小窓があるとします。あなたは「荷物」を置くためだけにその小窓を開けたいのに、もし誰かがその小窓から「ついでにドアの鍵も開けておいて」と書いたメモを差し込んできたらどうでしょう?
これが「OSコマンドインジェクション」の正体です。
プログラムの世界では、特定の機能を動かすために、OS(WindowsやLinuxなど)に対して「このコマンドを実行してね」とお願いすることがあります。その「お願いする窓口」に、攻撃者が悪意のある命令(バッククォート「`」やパイプ「|」など)を混ぜ込むことで、あなたの代わりに、攻撃者がやりたい放題の命令をOSに実行させてしまうのです。
なぜ「メタ文字」が危ないのか?
例えば、入力されたファイル名をそのままOSに渡すプログラムがあるとします。攻撃者は、本来の命令の後に、パイプ記号「|」を添えてこんな風に送ります。
filename.txt | rm -rf /
OSからすれば、「ファイルを表示した後に、全部消去(rm -rf)せよ」という一つの指示に見えてしまうんです。これが、メタ文字と呼ばれる「命令と命令を繋ぐ記号」をそのまま素通ししてしまうことの危険性です。
—
2. 「exec関数」という誘惑を断ち切ろう
開発現場でよくあるミスが、OSの機能を呼び出すための「exec系関数(exec(), system(), passthru() など)」を、深く考えずに使ってしまうことです。これらは非常に強力ですが、「何でも実行できてしまう」という最強の弱点を持っています。
ダメな例:OSに丸投げする書き方
// ユーザーからの入力をそのままOSに渡すのは「鍵を開けてください」と言っているようなもの
$filename = $_GET[‘file’];
system(“cat /var/www/uploads/” . $filename);
これだと、もし攻撃者が filename.txt; rm -rf / と送ってきたら、サーバーは即座に大ダメージを受けてしまいます。
—
3. 「ライブラリ」という安全な防犯システムを使おう
では、どうすればいいのでしょうか? 一番の解決策は、「OSに直接命令を送らないこと」です。
多くのプログラミング言語には、OSコマンドを直接叩かなくても、目的の処理(ファイルの読み込み、画像のリサイズなど)を実行できる「ライブラリ」や「標準関数」が用意されています。
良い例:OSに頼らず、言語の機能で解決する
// OSコマンドを使わずに、プログラム言語の標準機能を使う
$filename = $_GET[‘file’];
$path = “/var/www/uploads/” . basename($filename); // ファイル名のみを抽出して安全を確保
if (file_exists($path)) {
echo file_get_contents($path); // これならOSコマンドを混ぜ込む余地がありません
}
「どうしてもOSの機能を使わなければならない」というレアなケースでは、「ホワイトリスト方式」を採用しましょう。
- ホワイトリスト方式の考え方:
「このコマンド以外は絶対に受け付けない」という名簿をあらかじめ作っておくことです。
// 許可されたコマンド以外は実行させない
$allowed_commands = [‘view_log’, ‘check_status’];
$user_input = $_POST[‘action’];
if (in_array($user_input, $allowed_commands)) {
// 許可されたものだけを実行する
shell_exec(“/usr/bin/” . $user_input);
} else {
die(“不正なアクセスです。”);
}
—
4. 一歩ずつ、セキュリティを強固に
今日のまとめとして、明日からの開発で意識してほしい「3つの鉄則」を置いておきますね。
1. OSコマンドの実行を極力避ける: まずは「それ、標準関数でできないかな?」と疑うことから始めてください。
2. 入力を信じない: ユーザーから送られてくるデータは、すべて「泥棒が書いたメモかもしれない」と疑い、必ずチェック(バリデーション)を通しましょう。
3. 直接渡さない: もしコマンドを使うなら、引数を適切にエスケープ(escapeshellarg()など)し、メタ文字が無効化されるように細心の注意を払いましょう。
セキュリティ対策は、一度やって終わりではありません。家の鍵を毎日掛けるのと同じように、コードを書くたびに少しずつ意識を向けていく。そうすれば、あなたの書くプログラムは、攻撃者にとって「攻略困難な難攻不落の城」になっていくはずです。
最初から完璧を目指さなくて大丈夫です。まずは「入力をそのままOSに渡していないか?」という視点を持つところから、一歩ずつ進んでいきましょう!
コメント