家の鍵を「合鍵」で開けさせるな!OSコマンドインジェクションの恐ろしい罠
こんにちは!セキュリティの世界へようこそ。
今日は、開発現場でついやってしまいがちな、でも一度やらかすと家(サーバー)のすべてを泥棒に明け渡してしまう危険な扉——「OSコマンドインジェクション」についてお話しします。
専門用語を聞くと身構えてしまうかもしれませんが、大丈夫。まずは身近な例えから紐解いていきましょう。
—
「合鍵」を勝手に作らせないための防犯術
想像してみてください。あなたは自分の家の玄関に「宅配便の荷物を預かるための小窓」を作りました。
- 安全な状態: あなたは「荷物を置いてください」とだけメモを渡します。配達員は荷物を置いて帰ります。
- 危険な状態: あなたが「メモに書いたことをそのまま実行する」というルールを設けてしまったらどうでしょう?
- 悪意ある誰かが「荷物を置いた後、ついでに裏口の鍵も開けておいて」というメモを残したら?
- そのメモを見た(プログラムが実行した)配達員は、疑うことなく裏口を開けてしまいますよね。これが「OSコマンドインジェクション」の正体です。
プログラムの世界では、exec() や system() といった関数が、この「メモを読み上げて実行する配達員」の役割を果たしています。
なぜ「シェル」を介すと危険なのか?
多くのプログラミング言語には、OSのコマンドを直接叩く機能があります。例えば、PHPやPythonなどで、「ユーザーが入力したファイル名を使って、ファイルを変換する」ようなプログラムを書くとします。
ここでやってしまいがちなのが、シェル(OSを操作する司令塔)を間に挟んでしまうことです。
// 危険な例:ユーザーの入力をそのままシェルに渡している
$filename = $_GET[‘filename’];
// 「;」や「&」などの記号が入ると、コマンドが別の命令に書き換えられてしまう
system(“convert_image ” . $filename);
もし攻撃者が filename に image.jpg; rm -rf / なんて入力したらどうなるか……。
「画像を変換せよ」という命令の後に、「すべてのデータを削除せよ」という命令が追加され、OSがそれを律儀に実行してしまいます。これが「シェルを介したインジェクション」のメカニズムです。
—
対策の基本:「直接実行」と「配列」の魔法
では、どうすればこの「合鍵作り」を防げるのでしょうか?
答えは簡単です。「メモの内容を解釈させず、引数を直接渡す」ことです。
多くの言語では、コマンドと引数を「一つの長い文章」として渡すのではなく、「配列(リスト)」として渡す仕組みが用意されています。
Pythonでの安全な書き方例
import subprocess
ユーザーからの入力を受け取る
user_input = “image.jpg”
【重要】シェルを介さず、引数をリストとして渡す
これなら、「; rm -rf /」という文字列も「ファイル名の一部」として扱われるため、
悪意ある命令として実行されることはありません。
subprocess.run([“/usr/bin/convert_image”, user_input])
なぜこれで安全なのか?
配列で渡すと、OSは「コマンド」と「それに対するただのデータ」を明確に区別します。たとえデータの中に「裏口を開けろ」という指令が紛れ込んでいても、OSは「そんな名前のファイルは存在しないよ」とエラーを返すだけで、命令としては実行しないからです。
—
実務で守るべき3つの鉄則
現場でコードを書くとき、以下の3点を意識するだけで、あなたの守備力は格段に上がります。
1. シェルを介する関数は極力避ける
system, exec, popen などの「シェル経由」の関数は、強力な武器です。可能な限り、OSコマンドを使わないライブラリ(ファイル操作なら言語標準の関数など)で代用しましょう。
2. ユーザー入力を「信頼」しない
「まさかユーザーがコマンドを入力するはずがない」という思い込みは捨ててください。悪意ある攻撃者は、あらゆる入力フィールドを攻撃の入り口として狙っています。
3. 引数は必ず配列で渡す
どうしてもOSコマンドが必要な場合は、引数を分離できる関数を使いましょう。これが現代のWeb開発における「鉄の掟」です。
—
最後に:セキュリティは「疑うこと」から始まる
セキュリティ対策とは、完璧な壁を作ることではなく、「どうすれば相手(攻撃者)の意図を無効化できるか」という設計思想の積み重ねです。
最初は難しく感じるかもしれませんが、こうして一つずつ「なぜ危険なのか」「どうすれば防げるのか」を理解していけば、あなたは単なるエンジニアから、頼られる「守りのスペシャリスト」へと成長できます。
一歩ずつ、着実に学んでいきましょう。あなたの書くコードが、誰かの大切なデータを守る盾になるのですから。
それでは、また次回のレッスンでお会いしましょう!
コメント