FlawOpen

flawopen.com/Path Traversal/Node.js path.join()

Node.js path.join()으로 Path Traversal을 막을 수 있나요?

참고 페이지
짧은 답변

아니요. path.join()은 경로 세그먼트를 정규화하지만 ..이 의도한 디렉터리 밖으로 나가는 것을 막지 않습니다. 후보 경로를 기준 디렉터리에서 resolve한 뒤 상대 경로가 내부에 남는지 확인하세요. 이 문자열 기반 검사만으로 파일 시스템 심볼릭 링크를 제한할 수는 없습니다.

여전히 취약
const filePath = path.join(
  BASE_DIR, userFilename
);
// userFilename = "../../etc/passwd"
// still escapes BASE_DIR
수정됨 — 문자열 기준 포함 검사
const base = path.resolve(BASE_DIR);
const resolved = path.resolve(base, userFilename);
const relative = path.relative(base, resolved);

if (
  relative === '' ||
  relative === '..' ||
  relative.startsWith('..' + path.sep) ||
  path.isAbsolute(relative)
) {
  throw new Error("Invalid path");
}

path.join()만으로 충분하지 않은 이유

path.join()은 플랫폼 구분자로 경로 세그먼트를 결합하고 결과를 정규화합니다. 여러 경로에서 유효한 ..은 거부하지 않습니다. ../../etc/passwd를 기준 경로와 결합해도 바깥 경로가 될 수 있으므로 join()은 포함 여부를 검사하지 않습니다.

자주 묻는 질문

path.normalize()를 쓰면 도움이 되나요?

path.normalize()는 문자열의 .. 및 . 세그먼트를 정리하지만 결과가 허용된 기준 경로 안에 있는지 확인하지 않습니다. path.relative(base, resolved)를 비교하고 파일이 필요한 경우 빈 결과를 거부하세요. .., .. 뒤에 path.sep이 오는 접두사, 절대 상대 결과도 거부해야 합니다.

이 검사가 심볼릭 링크 탈출도 막나요?

아닙니다. path.resolve()와 path.relative()는 경로 문자열만 비교하며 파일 시스템 심볼릭 링크를 따라가지 않습니다. 공격자가 기준 디렉터리 안에 링크를 만들거나 바꿀 수 있다면 실제 대상을 검증하고 검사와 사용 사이의 경쟁을 막는 파일 접근 방식을 쓰세요. 가능하면 디렉터리 트리의 쓰기 권한을 제한하세요.

참고 자료

함께 보기 경로 조작(CWE-22): 동작 원리와 모든 언어에서의 예방법 — flawopen.com PHP include()와 사용자 제어 경로 Python os.path.join() 경로 탐색 수정 Zip Slip 취약점이란?