CyberFix Note
セキュアコーディング

OSコマンドインジェクションの仕組みと根本対策。シェルに渡さず引数を分離する

対象の目安: Webアプリ/バックエンドを実装する開発者 / 実務

リク編集長 / セキュリティ全般・戦略
・ 約13分で読めます
OSコマンドインジェクションの仕組みと根本対策。シェルに渡さず引数を分離する

アプリケーションが外部から受け取った入力を、そのままOSのシェルに渡して外部プログラムを実行する。この何気ない実装が、任意のOSコマンドを実行される深刻な欠陥につながります。これがOSコマンドインジェクション(CWE-78)です。攻撃が成立すれば、サーバー上でファイルの読み書き、認証情報の窃取、他システムへの足がかりの確保まで、アプリケーションの権限で可能なことがほぼすべて実行され得ます。

やっかいなのは、原理も対策も古くから知られているにもかかわらず、いまも現役で作り込まれ続けている点です。2024年にはネットワーク機器の未認証リモートコード実行の原因として大きな注目を集め、CISAとFBIが設計段階での排除を求める注意喚起を出すほど根強く残っています。この記事では、なぜこの脆弱性が成立するのかという機構から、SQLインジェクションと共通する根、そしてシェルを介さず引数を分離するという根本対策までを、実装の判断基準まで含めて順に整理します。

OSコマンドインジェクションが成立する機構

問題の核心は、OSコマンドという「命令」と、利用者が入力した「データ」を、文字列として連結してシェルに渡す点にあります。たとえばアプリケーションが、利用者の指定したホスト名に対して疎通確認を行う機能を、次のような発想で実装したとします。

# 危険な発想: 入力を連結してシェルに丸ごと渡す
command = "ping -c 1 " + 入力値
shell(command)

シェルは受け取った文字列を解釈し、; | && ` $(...) といった記号(シェルメタキャラクタ)を、コマンドの区切りや別コマンドの起動として扱います。つまりシェルにとっては、どこまでが開発者の意図した命令で、どこからが利用者の入力なのか区別がつきません。利用者がホスト名のつもりの欄に別のコマンドを続けて書けば、シェルはそれも独立した命令として実行します。命令とデータの境界が、文字列連結とシェルの解釈という二段構えで壊れてしまうのです。

MITREのCWE-78は、この脆弱性を「OSコマンドに使われる特殊要素の不適切な無害化」と定義しています。ポイントは、これが「特定の記号をうっかり通した」という表層的な話ではなく、「命令とデータを混ぜて文字列として組み立て、それをシェルに解釈させている」という構造の問題だという点です。だからこそ、後述するように危険な記号を個別に潰す発想ではなく、そもそもシェルに解釈させない設計が必要になります。

MITRE CWE-78は、アプリケーションが外部からの影響を受ける入力を用いてOSコマンドの全部または一部を構成する際、特殊要素を適切に無害化しないと、意図しないコマンドが実行され得ると説明しています。

SQLインジェクションと同じ根を持つ理由

OSコマンドインジェクションは、SQLインジェクションと兄弟の関係にあります。どちらもインタプリタ(SQLならデータベース、OSコマンドならシェル)に対して、命令とデータを混ぜた文字列を渡し、データを命令として解釈させてしまう点が同じだからです。攻撃の対象がSQL文かシェルコマンドかが違うだけで、根は一つです。

OWASPはこの共通性から、両者を同じ「インジェクション」というカテゴリでまとめています。OWASP Top 10では、インジェクションは2021年版でA03、2025年版ではA05として分類されており、SQLインジェクションやOSコマンドインジェクション、NoSQL、LDAP、ORMなどへのインジェクションがこの分類に含まれます。OWASPはインジェクションを、信頼できない入力がインタプリタに送られ、その入力の一部が命令として実行される欠陥として整理し、防止の第一原則を「データを命令やクエリから分離し続けること」としています。

OWASP Top 10:2025では、インジェクションはA05として分類されています(2021年版ではA03)。SQLインジェクションやOSコマンドインジェクションなどが同じカテゴリに含まれ、防止の要点として、安全なAPIの利用やインタプリタからのデータ分離が挙げられています。

この「命令とデータを分離する」という原理は、SQLインジェクション対策のプレースホルダとまったく同じ考え方です。SQLインジェクションの仕組みとプレースホルダによる分離を先に理解しておくと、OSコマンドインジェクションの対策も同じ発想の応用として腑に落ちます。

あわせて読みたい

SQLインジェクションとは何か。仕組み・攻撃手法・影響・対策を原理から徹底解説

なぜ作り込まれるのか

対策が確立しているのに脆弱性がなくならないのは、実装の都合上「シェルに渡すのが手軽だから」という理由が大きいです。多くの言語やライブラリには、コマンド文字列を一つ渡すだけでシェル経由で実行してくれる便利な関数が用意されています。パイプやリダイレクトをそのまま使えるため、シェルスクリプトの感覚で書けてしまいます。

ここに外部入力を連結すると、そのまま脆弱性になります。ファイル名、ホスト名、オプション値、アーカイブの展開先など、一見無害に見える入力でも、シェルに文字列として渡る限りメタキャラクタの解釈から逃れられません。開発時は正常系の入力しか試さないため、問題が表面化しないまま本番に出てしまいます。

CISAとFBIは2024年7月、ネットワーク機器で相次いだ未認証リモートコード実行を受けて、この種の欠陥を設計段階で排除するよう促す注意喚起(Secure by Design Alert)を公表しました。OSコマンドインジェクションは、利用者入力とコマンドの内容を明確に分離すれば以前から防げたにもかかわらず、いまなお広く残る脆弱性クラスだと指摘し、開発者に対して、生の文字列を組み立てるのではなく、コマンドと引数を分離する組み込みのライブラリ関数を使うよう求めています。

CISAとFBIのSecure by Design Alertは、OSコマンドインジェクションを設計段階で排除すべき脆弱性クラスと位置づけ、コマンドと引数を分離する組み込み関数の利用など、利用者入力をコマンドの内容から明確に分ける設計を求めています。

根本対策の組み立て方

根本対策は、シェルにコマンド文字列を解釈させないことに尽きます。実行ファイルと引数を別々の要素として渡せば、引数の中にどんな記号が入っていても、それは実行ファイルへの単なる引数の一部として扱われ、コマンドの区切りや別コマンドの起動として解釈されません。命令(実行するプログラム)とデータ(引数)を最初から分離する設計です。

  1. 1

    シェルを介さない実行APIで引数を配列として渡す

    実行ファイル名と各引数を、一つの文字列に連結せず、配列(リスト)として個別に渡すAPIを使います。多くの言語は、シェルを起動せずにプログラムを直接実行する仕組みを備えています。たとえばJavaのProcessBuilderはコマンドと引数を分けて受け取り、Pythonのsubprocessは引数リストを渡しshell=Falseを保つことで、シェルの解釈を経ずに実行します。引数に含まれる記号がメタキャラクタとして働かないため、境界が壊れません。

  2. 2

    そもそも外部コマンドの呼び出しを避ける

    実現したい処理が言語やライブラリの機能で代替できるなら、外部コマンドを呼ばないのが最も安全です。ファイル操作や圧縮展開、画像処理、HTTP通信などは、標準ライブラリや実績のあるライブラリで完結できることが多く、シェルもプロセス生成も不要になります。呼び出しをなくせば脆弱性の発生余地そのものが消えます。

  3. 3

    どうしても文字列を扱うなら許可リストとエスケープで補う

    設計上どうしても入力をコマンドに反映せざるを得ない場合は、取り得る値を列挙した許可リスト(allowlist)で照合し、想定外を拒否します。エスケープが必要なら、自前で組まず対象シェルに対応した実績あるライブラリの機能を使います。ただしこれらは分離ができない場合の補助であり、根本対策の代わりにはなりません。

  4. 4

    最小権限で実行する

    外部コマンドを実行するプロセスの権限を必要最小限に絞ります。専用の低権限アカウントで動かし、不要なファイルやネットワークへのアクセスを与えないことで、万一のすり抜け時にも被害を限定できます。根本対策の上に重ねる保険です。

  5. 5

    検証で塞がったことを確認する

    許可された自組織の検証環境で、記号を含む入力を与えても余計なコマンドが実行されないこと、引数として無害に扱われることを確認します。あわせて、文字列連結でコマンドを組み立てている箇所を静的解析やコードレビューで洗い出し、回帰を防ぎます。

OWASPのOS Command Injection Defense Cheat Sheetも、第一の防御として外部コマンド呼び出し自体を避けることを挙げ、避けられない場合はコマンドと引数を分離して渡す仕組み(JavaのProcessBuilderなど)を使い、各引数を個別に検証する方式を推奨しています。シェルを起動しなければ、& | などのメタキャラクタは単なる引数として扱われ、コマンド連結として働きません。

OWASPのOS Command Injection Defense Cheat Sheetは、可能なら外部コマンド呼び出しを避けることを最優先とし、必要な場合はコマンドと引数を分離して渡すAPIを用い、引数ごとに検証することを推奨しています。シェルを介さないため、シェルメタキャラクタが解釈されないと説明しています。

シェル経由の文字列連結と、実行ファイルと引数を分離する方式の違いを、言語中立の擬似コードで対比します。

# 危険: 文字列を連結してシェルに解釈させる
shell("ping -c 1 " + host)

# 安全: 実行ファイルと引数を配列で分離し、シェルを介さない
exec(["ping", "-c", "1", host])   # host は常に1個の引数として扱われる

下の書き方では、host にどんな文字列が入っても、それはpingへの一つの引数にしかなりません。記号が含まれていても別コマンドとして起動する経路が存在しないため、境界が壊れません。これが根本対策である理由です。

入力検証は補助にとどまる理由

「危険な記号を入力段階で弾けば安全ではないか」という発想は自然ですが、これを主たる防御にはできません。理由はSQLインジェクションやXSSと同じで、拒否リスト(denylist)方式には回避の余地が無数にあるからです。エンコードの違い、想定していないメタキャラクタ、シェルや環境ごとの挙動差などにより、列挙して弾く方式は取りこぼしが生じます。

一方、許可リスト方式で「安全と分かっている値だけを通す」のは有効な補助になります。ホスト名やファイル識別子のように取り得る形が限定できるなら、形式や候補を厳格に照合して想定外を拒否します。ただしこれも、あくまで分離という根本対策の上に重ねる一段目の壁です。入力検証の役割分担と限界は、XSSやSQLインジェクションを含めた共通の土台として別記事で整理しています。

あわせて読みたい

入力バリデーションと出力エスケープの原則。入口で検証し、出口で文脈別にエスケープする

注意

入力検証や許可リストだけを根本対策と誤解しないでください。危険な記号を弾く発想は回避されやすく、単独では防ぎきれません。根本対策はあくまで、シェルを介さず実行ファイルと引数を分離して渡すことです。入力検証はその上に重ねる補助と位置づけてください。

IPAの「安全なウェブサイトの作り方」も、OSコマンドインジェクションについて、シェルを起動できる言語機能の利用を避けることを根本的解決とし、それが難しい場合にチェック機能を設けることを保険的対策として区別しています。根本的解決を必ず据えた上で、保険的対策を重ねるという順序が要点です。

IPAの「安全なウェブサイトの作り方」は、OSコマンド・インジェクション対策として、シェルを起動する言語機能の利用を避けることを根本的解決に挙げ、シェルの呼び出しが避けられない場合の入力チェックを保険的対策として整理しています。

なお、脆弱性の検証や再現テストは、必ず自分が管理する環境、または明示的に許可を得た対象に対してのみ行ってください。許可のないシステムへの調査や攻撃は、不正アクセス禁止法をはじめとする法令に抵触します。

まとめ

OSコマンドインジェクションは、外部入力をOSコマンドの一部として解釈させることで任意のコマンドが実行される欠陥です。原因はSQLインジェクションと同じく、命令とデータを文字列として混ぜてしまう構造にあります。だからこそ対策も同じ原理に行き着きます。シェルに解釈させず、実行ファイルと引数を分離して渡す。可能なら外部コマンドの呼び出し自体をなくす。この設計を据えた上で、許可リストや最小権限を保険として重ねます。危険な記号を頑張って取り除くのではなく、そもそも混ざらない設計にすることが出発点です。

OSコマンドインジェクション対策チェックリスト

  • 外部コマンドの呼び出しを、言語やライブラリの機能で代替できないか検討したか
  • 実行時はシェルを介さず、実行ファイルと引数を配列で分離して渡しているか
  • コマンド文字列を連結してシェルに渡している箇所をすべて洗い出したか
  • 分離できない箇所は許可リストで検証し、エスケープは実績あるライブラリに任せているか
  • コマンド実行プロセスの権限を必要最小限に絞っているか
  • 入力検証を根本対策ではなく補助として位置づけているか
  • 記号を含む入力で余計なコマンドが実行されないことを検証環境で確認したか

インジェクション全般の考え方や、同じ原理に基づく他の脆弱性への広げ方は、次の記事とあわせて読むと理解が定着します。

あわせて読みたい

OWASP Top 10とは。開発者が押さえるべきWebの代表的リスクと対策を一気に理解する

あわせて読みたい

パストラバーサルとは何か。基準ディレクトリの外へ解決されるパスの脆弱性と根本対策を解説

出典・参考

この記事をシェア

関連する記事

セキュアコーディング

入力バリデーションと出力エスケープの原則。入口で検証し、出口で文脈別にエスケープする

セキュアコーディングの土台である入力バリデーションと出力エスケープを、それぞれの役割の違いから整理します。入口の検証は防御の一段目、出口の文脈別エスケープがXSSやインジェクションの根本対策である理由を、原理と実務の判断基準まで掘り下げます。