flawopen.com/경로 탐색/Python os.path.join()
Python의 os.path.join()은 경로 탐색을 막을까요?
아니요. 기준 디렉터리를 방이라고 하면 ..은 부모 방을 거쳐 밖으로 나가고, 절대 경로는 새 주소라서 os.path.join()이 시작 방을 버립니다. 파일을 열기 전에 최종 경로를 해석하고 허용된 디렉터리 안인지 확인하세요.
import os base_dir = "/srv/uploads" user_filename = "../../outside/demo.txt" path = os.path.join(base_dir, user_filename) # Relative traversal can leave the base. path = os.path.join(base_dir, "/outside/demo.txt") # An absolute segment discards base_dir.
import os
def resolve_under_base(base_dir, user_filename):
if not isinstance(user_filename, str) or not user_filename:
raise ValueError("Invalid filename")
base = os.path.realpath(base_dir)
candidate = os.path.realpath(
os.path.join(base, user_filename)
)
try:
inside = os.path.commonpath((base, candidate)) == base
except ValueError: # e.g. different Windows drives
inside = False
if not inside or candidate == base:
raise ValueError("Invalid filename")
return candidate
고려할 두 가지 경로 결합 동작
..은 상대 경로를 의도한 디렉터리 밖으로 이동시킬 수 있습니다. Python 문서에 따르면 절대 경로 구성 요소는 os.path.join()이 앞선 구성 요소를 버리게 합니다. Windows의 드라이브와 루트 규칙은 POSIX와 다릅니다. pathlib.Path(base) / user_input도 같습니다. 결합과 정규화는 권한 검사가 아닙니다.
commonpath()가 문자열 접두사보다 안전한 이유
비교 전에 신뢰 기준과 후보 경로를 해석하세요. os.path.commonpath()는 문자 접두사가 아니라 경로 구성 요소를 비교하며 루트나 드라이브가 맞지 않으면 ValueError를 발생시킵니다. 해당 입력을 거부하세요. realpath()는 검사 당시 링크를 해석하지만 이후 변경은 막지 못합니다. 다른 사용자가 트리를 수정할 수 있다면 적절한 디스크립터 접근과 no-follow를 사용하세요.
- ✓빈 경로나 문자열이 아닌 입력을 거부하세요.
- ✓기준과 후보를 해석하고
commonpath((base, candidate)) == base를 요구하세요.ValueError와 파일이 필요할 때 기준 디렉터리 자체를 거부하세요. - ✓경로 범위 확인과 권한 부여를 분리하고 필요한 파일과 작업만 허용하세요.
- ✓신뢰할 수 없는 사용자가 링크를 바꿀 수 있으면 적절한 파일 디스크립터 API로 검사/사용 경쟁을 막으세요.
자주 묻는 질문
네. Path의 / 연산자는 다음 경로가 절대 경로이면 앞선 구성 요소를 버립니다. 결과를 해석해 허용 기준 안인지 확인하세요.
아니요. 검증 당시 링크만 해석하며 다른 프로세스가 파일을 열기 전에 변경할 수 있습니다. 쓰기 권한을 제한하거나 지원되는 경우 no-follow 디스크립터 상대 작업을 사용하세요.