Path Traversal(경로 순회) 공격은 악의적인 사용자가 파일 경로를 조작하여 허용된 디렉토리 밖의 시스템 파일에 접근하려는 보안 공격입니다.
공격 원리
정상적인 사용
의도된 사용: documents 폴더 안에만 저장
curl -F "path=documents/business-license" -F "file=@file.pdf" /api/files/upload
→ S3에 저장: documents/business-license/uuid_file.pdf ✅
공격 시도 (차단하지 않으면 위험!)
공격 1: 상위 디렉토리 접근 시도
curl -F "path=documents/../../etc/passwd" -F "file=@malicious.txt" /api/files/upload
→ 의도: /etc/passwd 파일 덮어쓰기 (시스템 계정 정보 파괴!) ❌
공격 2: 절대 경로로 시스템 파일 접근
curl -F "path=/etc/shadow" -F "file=@malicious.txt" /api/files/upload
→ 의도: /etc/shadow 파일 덮어쓰기 (비밀번호 정보 탈취!) ❌
공격 3: Windows 경로 구분자 사용
curl -F "path=documents\\..\\..\\Windows\\System32" -F "file=@virus.exe" /api/files/upload
→ 의도: Windows 시스템 폴더에 악성 파일 업로드 ❌
우리 코드의 방어 메커니즘
private void validatePath(String path) { if (!StringUtils.hasText(path)) { throw new IllegalArgumentException("저장 경로가 비어있습니다"); } // 경로 보안 검증 (path traversal 공격 방지) if (path.contains("..") || path.startsWith("/") || path.contains("\\\\")) { throw new IllegalArgumentException("유효하지 않은 저장 경로입니다: " + path); } }
차단되는 패턴
- .. 포함 - 상위 디렉토리 접근 차단 documents/../../../etc/passwd ❌ ../../sensitive/data ❌ documents/test/../../../ ❌
- /로 시작 - 절대 경로 차단 /etc/passwd ❌ /var/log/system.log ❌ /root/.ssh/id_rsa ❌
- \ 포함 - Windows 경로 구분자 차단 documents\..\..\..\Windows ❌ C:\Windows\System32 ❌
허용되는 패턴
documents/business-license ✅
documents/id-card ✅
images/profile ✅
contracts/2024 ✅
실제 피해 사례 (차단하지 않으면)
시나리오 1: 시스템 파일 덮어쓰기
공격자가 시도
curl -F "path=../../etc/hosts" -F "file=@fake_hosts.txt" /api/files/upload
결과 (방어 없으면)
→ /etc/hosts 파일이 악성 내용으로 변경됨
→ 모든 DNS 조회가 공격자 서버로 리다이렉트됨
→ 피싱 사이트로 연결, 데이터 탈취
시나리오 2: 설정 파일 접근
공격자가 시도
curl -F "path=../../../application.yaml" -F "file=@malicious.yaml" /api/files/upload
결과 (방어 없으면)
→ 애플리케이션 설정 파일 변조
→ DB 비밀번호, API 키 등 탈취 가능
→ 서비스 완전 장악
시나리오 3: 소스 코드 덮어쓰기
공격자가 시도
curl -F "path=../../src/main/java/Controller.java" -F "file=@backdoor.java" /api/files/upload
결과 (방어 없으면)
→ 실제 코드가 백도어 코드로 변경됨
→ 재배포 시 백도어가 포함됨
우리 코드의 안전성
✅ 모든 공격 시도가 차단됨
$ curl -F "path=../../etc/passwd" -F "file=@test.txt" http://localhost:8080/api/files/upload
{
"success": false,
"code": "E400",
"message": "유효하지 않은 저장 경로입니다: ../../etc/passwd"
}