何が起きるか

OSコマンドインジェクションは、アプリが利用者の入力を実行用の文字列へ入れ、その中の記号や命令までOSで実行してしまう問題です。

Bashを使う説明用の例では、攻撃者がreport-$(id).txtというファイル名を送ります。サーバーがその名前をコードへ直接埋め込み、echo "report-$(id).txt"をBashへ渡すと、Bashは$(id)をコマンド置換として評価します。idは実行ユーザーを表示するコマンドで、単に名前を出力するはずがサーバーでその命令まで動きます。

実行場所はアプリのサーバーで、通常はアプリのOSユーザーの権限です。同じ経路で設定ファイルの読み取り命令を動かせ、読み取り結果がHTTP応答や外部通信で攻撃者へ届けば、接続パスワードなどが漏れるおそれがあります。入力がコードへ埋め込まれることと、結果を取得できることがそれぞれ条件です。引用した変数からデータとして展開するだけなら、値の中の$(id)が自動で再実行されるわけではありません。 具体的には、環境変数FILE_NAMEへその名前を保存し、Bashへ渡すコードをecho "$FILE_NAME"に固定する方式です。Bashが先に読むコードには$(id)がなく、変数の値を展開した後で再びコードとして読み直さないため、名前の一部として表示されます。

正常ならどう動くか

画像変換を例にすると、利用者がアップロードした画像を、アプリが固定の変換ライブラリへ渡して処理します。外部プログラムを起動する必要がある場合も、実行ファイルを固定し、利用者が指定したファイル名は引数配列の一要素として渡します。ファイル名を含む文字列全体をシェルのコードとして実行しません。 たとえばプロセス起動APIへ、固定ツールのパスと[入力ファイル名]を別々に渡し、シェルを使わない設定で起動します。OSがそのツールを起動し、ツールは配列の要素を一つの引数として受け取るため、途中にBashが文字列を評価する処理はありません。

不備と攻撃が成立する流れ

攻撃者がアップロード時のファイル名を操作できる構成を考えます。アプリがその名前をコマンド文字列へ連結し、シェルへ渡すと、名前に含まれる記号が命令の区切りやコマンド置換として解釈される場合があります。ファイル名を受け取ることではなく、その文字列を実行用コードとして解釈する処理が不備です。

Bashを使う説明例では、report-$(id).txtという名前をコード中へ直接埋め込むと、Bashが$(id)をコマンド置換として処理し、実行ユーザーを表示するidコマンドを動かします。一方、引用した変数からその名前をデータとして展開するだけなら、値の中の$(id)が自動で再実行されるわけではありません。

シェルを使わず引数配列を渡しても、実行先ツールが利用者入力をオプションと解釈すると、選べる機能を変えられる場合があります。シェルの命令解釈をなくすことと、ツールに許す機能を限定することは、それぞれ確認します。 たとえばファイル名として--helpを渡すと、ツールによってはファイルを開かずヘルプを表示します。これは無害な機能選択の例で、危険な機能を選べるかは実行先ツールのオプション仕様によります。

起こり得る被害と条件

秘密取得の例では、攻撃者の命令がアプリの設定ファイルを読みます。その内容をHTTPレスポンスへ含めたり、外部のサーバーへ送信したりできれば、攻撃者は接続パスワードなどを入手できます。ファイルの読み取り権限と、攻撃者へ内容が届く経路の両方が必要です。

改変の例では、攻撃者の命令がアプリから書き込める公開ページや設定ファイルを更新します。ファイルが実際に配信・読み込みされると、表示内容やアプリの動作が変わります。別プロセスを起動できる構成なら、攻撃者の処理をサーバーで動かされるおそれもあります。

命令は通常、アプリを動かすOSユーザーの権限で実行されます。管理者権限やコンテナ外へのアクセスを自動で得るわけではなく、ファイル権限や実行環境の制限に従います。

防ぎ方と、防げる理由

画像変換などは、その処理専用のライブラリを使い、ファイル名からコマンド文字列を作る必要をなくします。外部プログラムが必要なら、実行ファイルのパスを固定し、シェルを無効にして起動します。ファイル名は引数配列の一要素として渡すため、Bashなどが入力中の区切り記号やコマンド置換を評価する経路をなくせます。

ただし実行先のプログラムは、引数をオプションとして解釈する場合があります。ファイル名の型や許可機能を制限し、対応するツールでは--以降をオプションでない引数として扱わせます。--に対応しないツールへ一般化せず、そのツールの引数仕様を確認します。シェルを避けることと、ツールが実行できる機能を制限することを両方行います。

よくある誤解

引用符で囲めば常に安全とは限りません。例えばBashのダブルクォート内でもコマンド置換は動きます。一方、引用した環境変数の値に含まれる記号が自動でシェルコードとして再評価されるわけではなく、コード生成とデータ展開を区別する必要があります。 名前を環境変数FILE_NAMEへ保存し、コードをecho "$FILE_NAME"に固定する方式と、名前そのものをechoのコード文字列へ埋め込む方式を比較します。前者は展開した値を再びシェル構文として解析せず、後者は$(id)を含むコードを最初から解析する点が異なります。

実装の確認点

exec、system、shell:true、自動処理のrunなど、文字列を命令として渡す箇所を確認します。ファイル名やフォーム入力がどの変数を通ってコマンドへ入るかを追い、シェルが実行する文字列とツールが受け取る引数を分けて確認します。読み取り・書き込みできるファイルと、出力が攻撃者へ届く経路も調べます。

出典・確認範囲

  1. [1] Web Security Academy: OSコマンドインジェクションPortSwigger / 確認日 2026-10-29