FlawOpen

flawopen.com/Command Injection/exec() vs execFile()

Node.js의 exec()와 execFile()는 어떻게 다른가요?

참고 가이드
짧은 답변

exec()는 문자열 하나를 받아 shell을 통해 실행하므로 pipe와 wildcard를 쓸 수 있지만, 신뢰할 수 없는 입력의 shell 메타문자가 명령을 바꿀 수도 있습니다. execFile()은 기본적으로 shell을 시작하지 않고 지정한 프로그램에 인자 배열을 전달합니다. shell 파싱은 막지만 모든 위험이 사라지는 것은 아닙니다. 대상 프로그램은 인자를 옵션으로 해석하거나 예상하지 못한 값을 처리할 수 있습니다.

비교

exec() — shell 사용
exec(`ls ${dir} | grep .txt`,
  (err, out) => {...})
// pipe는 작동하지만 메타문자도 해석됩니다.
execFile() — 기본적으로 shell 없음
execFile('ls', [dir],
  (err, out) => {...})
// shell은 없지만 ls가 dir 인자를 해석하는 방식은 검증해야 합니다.
// break out of its own argument

shell 기능이 꼭 필요한 경우

명령에 pipe, glob 확장 또는 shell 리디렉션이 필요하다면 execFile()로 이를 직접 표현할 수 없습니다. Node.js에서 각 단계를 별도 프로세스로 실행하고 stream을 연결해 파이프라인을 구성하는 편이 좋습니다. exec()가 불가피하다면 신뢰할 수 없는 입력을 명령 문자열에 넣지 마세요. shell이 없는 인자 배열도 인자 인젝션을 막지는 못합니다. 대상 프로그램에 맞게 각 값을 검증하고, 지원되는 경우 --를 사용하며, 허용할 명령과 옵션을 제한하세요. shell: true는 보통 shell을 실행하지 않는 API에서도 shell을 켭니다. 이 모드에서는 검증되지 않은 입력을 전달하지 마세요.

자주 묻는 질문

Windows 배치 파일은 보안 경계를 바꿉니다

Windows에서는 .bat 및 .cmd 파일을 execFile()로 직접 실행할 수 없습니다. Node 문서에 따르면 exec(), cmd.exe 또는 shell을 활성화한 spawn()으로 호출해야 하며, 이 경로에서는 명령 인터프리터 파싱이 다시 적용됩니다. 네이티브 실행 파일이나 직접 API를 우선하세요. 배치 파일 호출이 불가피하다면 스크립트 경로와 인수를 고정하거나 해당 스크립트의 정확한 계약에 따라 검증하세요. Node는 shell: true와 인수 배열을 함께 전달하는 방식도 deprecated로 지정했습니다(v22.15.0 및 v23.11.0부터).

spawn()은 execFile()과 같은가요?

spawn()도 기본적으로 shell 없이 실행되며 인자 배열을 받습니다. 실질적인 차이는 주로 출력 처리입니다. spawn()은 stream으로 출력하고 execFile()은 출력을 버퍼링합니다. 둘 다 대상 프로그램이 인자를 옵션으로 해석하는 것을 막지는 않으므로 해당 프로그램에 맞게 검증하세요. shell: true를 지정하면 shell 파싱이 다시 활성화됩니다.

참고 자료

함께 보기 OS 명령어 인젝션(CWE-78): 동작 원리와 모든 언어에서의 예방법 — flawopen.com Java Runtime.exec()와 Command Injection: 무엇이 다른가? Node.js exec()의 명령 주입 위험