명령어 주입 (Command Injection)

Command Injection

설명

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가 입력을 옵션으로 해석하지 않게 합니다. 실행 파일의 위치와 조회할 디렉터리의 접근 권한은 별도로 관리해야 합니다.

참조