Cursor가 파일 다운로드에 경로 탐색(Path Traversal)을 계속 작성하는 이유
요약
AI 에디터들이 파일 다운로드 엔드포인트에서 사용자 입력 경로를 직접 처리하여 Path Traversal 취약점을 발생시키는 경향이 있습니다. 이를 방지하려면, 파일명에서 디렉토리 부분을 제거하고 전체 경로를 해석(resolve)한 후, 해당 경로가 의도된 업로드 디렉토리 내에 존재하는지 반드시 검증해야 합니다.
핵심 포인트
- 사용자 입력 경로를 신뢰하지 말고 basename만 사용하세요.
- 전체 경로를 resolve하여 의도된 디렉토리 시작 여부를 확인하는 것이 필수입니다.
- Python에서는 os.path.realpath()와 같은 방식으로 처리할 수 있습니다.
- Semgrep이나 SafeWeave 같은 도구로 취약점을 사전에 탐지해야 합니다.
요약 (TL;DR)
- AI 에디터들은
req.query.file값을 파일 시스템 경로로 직접 전달하는 파일 다운로드 엔드포인트를 만드는 것을 좋아합니다. - 이 한 줄의 코드가 공격자가
../../../../etc/passwd를 요청하여 업로드 폴더 밖으로 나갈 수 있게 합니다. - 해결책은 지루합니다: 경로를 해석(resolve)한 다음, 여전히 의도했던 디렉토리 내에 존재하는지 확인해야 합니다.
지난주에 Cursor에게 사이드 프로젝트에
파일명은 절대 신뢰하지 마세요. 파일명에서 디렉토리 부분을 제거(strip)하여 기본 이름(basename)만 남기고, 전체 경로를 해석(resolve)한 다음, 아무것도 읽기 전에 그 결과가 의도했던 디렉토리 내에 여전히 존재하는지 확인해야 합니다.
const path = require('path');
const UPLOAD_DIR = path.resolve(__dirname, 'uploads');
...
여기서 두 가지 것이 작동하고 있습니다. path.basename은 모든 디렉토리 부분을 버리기 때문에, ../../etc/passwd는 passwd가 됩니다. startsWith 검사는 만약을 대비한 보증책입니다: 설령 무언가가 빠져나가더라도, 업로드 디렉토리로 시작하지 않는 해석된 경로는 거부됩니다.
스택이 Python이라면 같은 아이디어가 적용됩니다:
import os
BASE = os.path.realpath('uploads')
...
패턴은 모든 언어에서 동일합니다: 먼저 해석(resolve)하고, 그 다음 포함 여부를 검증하는 것입니다. ..와 심볼릭 링크(symlinks)가 읽을 때와 다르게 해석되기 때문에 원시 문자열(raw string)을 검증하지 마세요.
저는 SafeWeave를 실행하여 이러한 종류의 버그를 잡고 있습니다. 이것은 Cursor 및 Claude Code에 MCP 서버로 연결되어, 제가 다음 작업으로 넘어가기 전에 엔드포인트가 기록되는 순간 경로 탐색(path traversal)을 플래그 지정합니다. 그렇다고 해도, semgrep을 사용한 기본적인 pre-commit hook만으로도 이 게시물에 있는 대부분의 내용을 포착할 수 있습니다. 중요한 것은 어떤 도구를 사용하든 일찍 발견하는 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기