OSコマンドインジェクションとは?起きるパターンと対策法

OSコマンドインジェクションとは?起きるパターンと対策法

#セキュリティ#Web開発#脆弱性対策

OSコマンドインジェクションは、成立するとサーバ上で任意のコマンドを実行される、影響の大きい脆弱性です。 対策の要は、外部コマンドをシェル経由で呼ばない設計にできているかどうかです。

OSコマンドインジェクションとは

OSコマンドインジェクション:アプリケーションが外部コマンドを呼び出す際に、ユーザー入力を混ぜてコマンド文字列を組み立てることで、攻撃者が任意のコマンドを実行できてしまう脆弱性です。 シェルを介してコマンドを組み立てる設計が根本原因になります。

攻撃者は ; rm -rf| curl$(...) などシェルのメタ文字を使い、本来のコマンドに別のコマンドを継ぎ足します。 成立するとサーバの乗っ取り、ファイルの読み取りや破壊、内部ネットワークへの侵入の足がかりといった被害につながります。 SQLインジェクションがデータベースに閉じるのに対し、OSコマンドの実行はサーバそのものを掌握されるため、影響範囲がさらに広くなります。

OSコマンドインジェクションが起きるパターン

典型的な混入箇所は次のとおりです。

  • ユーザー入力を連結したコマンド文字列を、シェル経由で実行する関数に渡している(system("convert " + filename)execRuntime.execのシェル呼び出しなど)
  • ファイル名やホスト名、オプションとして受け取った値を、画像変換やアーカイブ展開などの外部ツールにそのまま渡している
  • pingnslookupなどOSコマンドをラップした機能で、宛先の入力を検証せずに渡している
  • バッチ処理やcron連携で、DBやファイルから読んだ値をコマンドに組み込んでいる
  • ライブラリの内部でシェルを起動する呼び出し方(shell=Trueなど)を使っている

共通するのは「入力がコマンドの引数ではなくシェルの構文として解釈される経路がある」という点です。

対策法

原則は「シェルを経由せず、コマンドと引数を分離して渡す」ことです。

  • コマンドと引数を配列で渡すAPIを使い、シェルを起動しない(execFilesubprocess.run([...], shell=False)など)。これが本命の対策になる
  • そもそも外部コマンドを使わず、言語やライブラリのネイティブAPIで代替できないか検討する
  • 引数として渡す値は、許可リストや型(数値、固定の選択肢)で検証してから使う
  • 実行するプロセスの権限を最小化し、コマンドが成立しても被害範囲を狭める(被害軽減策)

入力のサニタイズでメタ文字を除去する方法は抜け漏れが起きやすく、本命は引数配列でシェルを介さないことです。 まず外部コマンド呼び出しの一覧を洗い出し、シェル経由の箇所を配列渡しへ置き換える順序が費用対効果に優れます。

Rails での事例

Ruby では、system` (バッククォート) に1つの文字列を渡すとシェル経由で解釈されます。 そこにユーザー入力を連結するのが危険な形です。

# 危険:filename にシェルのメタ文字が含まれると別のコマンドが実行される
def convert
  filename = params[:filename]
  system("convert #{filename} output.png")
end

Ruby の対策は言語仕様に組み込まれています。 system にコマンドと引数を**別々の引数(配列相当)**で渡すと、シェルを起動せずプロセスを直接生成するため、入力はただの引数文字列として扱われます。

def convert
  filename = params[:filename]
  system("convert", filename, "output.png")
end

標準出力やエラーも扱いたい場合は Open3.capture3 を同じ多引数形式で使います。

stdout, stderr, status = Open3.capture3("convert", filename, "output.png")

なお、多引数形式でもコマンドの「オプション」として解釈される余地は残ります(-danger のような値を渡されるケース)。 引数の先頭が - でないことの検証や、許可リストでの絞り込みを重ねます。 どうしても文字列でシェルに渡さざるを得ない箇所は Shellwords.escape でエスケープしますが、これは最後の手段で、多引数形式への書き換えを先に検討します。

まとめ

OSコマンドインジェクション対策の要点を整理します。

  • 根本原因は「入力がシェルの構文として解釈される経路があること」。ユーザー入力を連結したコマンド文字列をシェルに渡す形が危険の中心になる
  • 本命の対策はコマンドと引数を分離して渡すこと。シェルを起動しなければ、メタ文字はただの引数文字列として扱われる
  • メタ文字を除去するサニタイズは抜け漏れが起きやすく、最後の手段。先に多引数形式・配列渡しへの書き換えを検討する
  • 多引数形式でも - で始まる値がオプションとして解釈される余地は残る。許可リストや型での検証を重ねる
  • 実行プロセスの権限最小化で、万一成立したときの被害範囲を狭めておく

外部コマンドの呼び出し箇所は systemexec・バッククォート・shell=True などの検索で機械的に洗い出せます。点検の入り口が明確な脆弱性なので、まず一覧を作るところから始めるのが近道です。

関連して読める記事

「外部入力が構文として解釈されて想定外の動作に至る」という構図は、SQLインジェクションとまったく同じです。影響範囲がデータベースに閉じるか、サーバ全体に及ぶかが両者の違いになります。同じくサーバ上でのコード実行に至る脆弱性としてはサーバサイドテンプレートインジェクション(SSTI)があり、こちらは入力がテンプレートの構文として評価される経路で成立します。また、ファイルパスという別の「構造」に入力が混ざるパストラバーサルも、原因と対策の型が共通しているのでセットで整理できます。

SQLインジェクションとは?起きるパターンと対策法

SQLインジェクションの仕組みと起きるパターン、そしてプレースホルダ(パラメータ化クエリ)を軸にした対策を整理します。Rails(Active Record)で穴が開く where の文字列展開と order のカラム名、その安全な書き方を具体的なコード例で示します。

2026年7月22日

サーバサイドテンプレートインジェクションとは?起きるパターンと対策法

サーバサイドテンプレートインジェクション(SSTI)の仕組みと起きるパターン、テンプレートを固定してユーザー入力を変数として渡す対策を整理します。Railsでユーザー由来の文字列を ERB.new で評価してしまう危険例と、固定テンプレート+locals での安全な実装、ロジックレステンプレートの使いどころまで具体的なコードで示します。

2026年7月24日

パストラバーサルとは?起きるパターンと対策法

パストラバーサル(ディレクトリトラバーサル)の仕組みと起きるパターン、正規化後の範囲チェックと許可リストを軸にした対策を整理します。Railsで send_file にファイル名パラメータをそのまま渡してしまう危険例と、File.expand_path による正規化+前方一致の検証、入力を「パス」ではなく「ID」にする設計まで具体的なコードで示します。

2026年7月24日
この記事をシェア