コマンドインジェクション

コマンドインジェクション

説明

Node.jsの child_process.spawn や spawnSync で {shell: true} または {shell: '/bin/sh'} を指定すると、外部コマンドがシェルを介して実行されます。ユーザー入力に含まれる特殊文字がコマンドとして解釈され、攻撃者に意図しない操作を挿入されるおそれがあります。

想定される影響

  • 悪意のあるコマンドによるサーバーファイルへのアクセス、削除、情報漏えい
  • 機密データの漏えい、改ざん、破壊
  • リソース枯渇やプロセスの終了によるサービス停止

対処方法

  • 固定の実行ファイルを使い、{shell: false} を指定して引数を配列で渡してください。
  • シェルが必要な場合は、信頼できる許可リストでコマンドと引数を制限してください。
  • 外部入力をコマンドや引数に使う前に検証し、クォート処理だけに依存しないでください。

例

変更前

javascript
const { spawn } = require("child_process");
const app = require("express")();

app.get("/list", (req, res) => {
  // リクエストの入力がシェルで解釈される引数になります。
  const userInput = req.query.path;
  const p = spawn("ls", ["-lh", userInput], { shell: true });
});

変更後

javascript
const { spawn } = require("child_process");
const app = require("express")();

app.get("/list", (req, res) => {
  const userInput = req.query.path;
  if (typeof userInput !== "string" || !/^[a-zA-Z0-9_-]+$/.test(userInput)) {
    return res.sendStatus(400);
  }
  // 検証した引数を固定の実行ファイルに渡し、シェルは使いません。
  const p = spawn("ls", ["-lh", "--", userInput], { shell: false });
});

解説:

  • 変更前: shell: true によって、入力中の特殊文字がシェルでコマンドとして解釈される可能性があります。
  • 変更後: 既定値の shell: false でシェルによる解釈を避け、入力形式を制限しています。-- は ls が入力をオプションとして扱うことを防ぎます。実行ファイルの場所や、一覧を取得するディレクトリへのアクセス権限は別途管理する必要があります。

参考資料