【入門編】OSコマンドインジェクション:言語別実行関数の危険性(exec, system, popen) – アプリケーションセキュリティ & 安全な開発防御ガイド

家の鍵を「合鍵」で開けさせるな!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開発における「鉄の掟」です。

—

最後に:セキュリティは「疑うこと」から始まる

セキュリティ対策とは、完璧な壁を作ることではなく、「どうすれば相手(攻撃者)の意図を無効化できるか」という設計思想の積み重ねです。

最初は難しく感じるかもしれませんが、こうして一つずつ「なぜ危険なのか」「どうすれば防げるのか」を理解していけば、あなたは単なるエンジニアから、頼られる「守りのスペシャリスト」へと成長できます。

一歩ずつ、着実に学んでいきましょう。あなたの書くコードが、誰かの大切なデータを守る盾になるのですから。

それでは、また次回のレッスンでお会いしましょう!

コメント

タイトルとURLをコピーしました