소프트웨어 개발보안 가이드 분석(2021) : 경로 조작 및 자원 삽입
경로 조작 및 자원 삽입
https://owasp.org/www-community/attacks/Path_Traversal
우선 소프트웨어 개발보안가이드(2021)에서는 이 공격 유형에 대해 이렇게 정의 하고 있습니다.
검증되지 않은 외부 입력값을 통해 파일 및 서버 등 시스템 자원에 대한 접근 혹은 식별을 허용할 경우, 공격자는 입력값 조작을 통해 시스템이 보호하는 자원에 임의로 접근할 수 있는 보안약점
소프트웨어 개발보안가이드(2021), 한국인터넷진흥원
공격자는 이 취약점을 이용하여 애플리케이션의 보안 경계를 벗어난 시스템의 중요한 파일이나 디렉토리에 접근하려 시도할 수 있습니다. 주로 웹 애플리케이션에서 파일 이름이나 경로를 사용자 입력으로 받아 처리할 때 나타나며, 공격자는 ../ 와 같은 상대 경로를 사용하여 애플리케이션의 루트 디렉토리를 벗어나려 시도합니다.
이 보안약점은 유입되는 공격 시도의 방향성에 따라 두 가지 유형으로 구분됩니다.
경로 조작(Path Traversal): 공격자가 애플리케이션의 기본 디렉토리를 벗어나, 파일 시스템 내의 다른 위치에 접근하여 민감한 정보를 읽거나 수정하는 행위입니다.
자원 삽입(Resource Injection): 공격자가 애플리케이션에 원치 않는 파일이나 스크립트를 삽입하여 실행시키는 행위입니다. 이는 웹 서버의 보안을 약화시키거나 다른 사용자들에게 악의적인 코드를 전파하는 수단으로 사용될 수 있습니다.
공격 시도의 방향성이 다르지만, 이러한 취약점의 공통적인 원인은 사용자 입력의 적절한 검증이 이루어지지 않는 것입니다. 특히, 파일 경로와 관련된 입력값을 검증 없이 사용할 때 발생 위험이 높아집니다. 공격자는 이러한 취약점을 이용하여 서버의 설정 파일, 데이터베이스 백업, 애플리케이션 소스 코드 등을 포함한 민감한 정보에 접근할 수 있습니다.
취약한 코드 예시
FileInputStream fis = new FileInputStream(request.getParameter("filename"));
우선 위 Java 코드처럼 사용자 입력을 통해 파일 이름을 받아서 해당 파일을 열어내는 기능을 수행하는 경우, request.getParameter("filename") 부분에서 사용자의 입력값을 검증 없이 사용하게 되면 취약점이 존재한다고 볼 수 있습니다. 공격자는 이 점을 이용하여 ../../../etc/passwd 와 같은 중요 시스템 파일의 경로를 입력값으로 제공함으로써 시스템 정보를 유출하거나 예상치 못한 오류를 유발하는 방식으로 시스템을 유도할 수 있죠.
예방 방법 및 안전한 코드 작성 가이드
위 공격에 대한 대응 방안으로 가장 바람직한 대응 방법은 "입력값 검증" 단계를 추가하는 것입니다. 특히, 파일 경로와 같은 중요한 정보를 다룰 때는 화이트리스트 기반의 검증 로직을 적용하여 안전한 값만을 허용해야 합니다. 그렇다면, 이러한 공격 유형에 대응하는 검증 로직은 어떤 것들이 있을까요?
a. 특수기호에 대한 입력값 검증
String filename = request.getParameter("filename");
if(isValidFilename(filename)) {
FileInputStream fis = new FileInputStream(getSafePath(filename));
} else {
throw new IllegalArgumentException("Invalid filename.");
}
// 사용자 입력값 검증
private boolean isValidFilename(String filename) {
return filename.matches("[\\w,\\s-]+[.][\\w]{3}");
}
// 안전한 경로 반환
private String getSafePath(String filename) {
return "/safe/directory/" + filename;
}
위 코드는 사용자의 입력값을 검증하는 isValidFilename 메소드와, 안전한 경로만을 반환하는 getSafePath 메소드를 통해 경로 조작 및 자원 삽입 공격을 효과적으로 방어합니다. 이러한 접근 방식을 통해, 애플리케이션은 오직 검증된 파일명에 대해서만 작업을 수행하게 됩니다.
b. 파일의 절대 경로 반환을 이용한 방어 로직 구현
import java.io.*;
import javax.servlet.http.*;
import java.nio.file.*;
public class SafeFileAccessServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fileName = request.getParameter("fileName");
File file = new File("/var/www/html/" + fileName).getCanonicalFile();
// 화이트리스트 기반의 파일 이름 검증
List<String> allowedFiles = Arrays.asList(
file.getCanonicalPath() + "document.txt",
file.getCanonicalPath() + "image.jpg"
);
if (allowedFiles.contains(file.getCanonicalPath())) {
// 파일이 허용된 디렉토리 안에 있는지 확인
if (file.getCanonicalPath().startsWith("/var/www/html/")) {
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
response.getWriter().println(line);
}
reader.close();
} else {
response.getWriter().println("파일 접근이 거부되었습니다.");
}
} else {
response.getWriter().println("접근이 거부되었습니다.");
}
}
}
File.getCanonicalFile() 메소드는 Java에서 파일의 정규 경로를 얻기 위해 사용됩니다. 이 메소드를 활용하여 Path Traversal 공격에 대한 방어 로직을 구현할 수 있습니다. 이 방법을 사용하면 파일의 심볼릭 링크를 따라가며 실제 경로를 찾아내어, 애플리케이션의 디렉토리를 벗어난 접근 시도를 차단할 수 있습니다.
- 사용자로부터 fileName 파라미터를 통해 파일 이름을 받습니다.
- 받은 파일 이름을 기반으로 File 객체를 생성한 후, .getCanonicalFile() 메소드를 호출하여 정규화된 파일 객체를 얻습니다.
- 파일의 정규화된 경로(getCanonicalPath())가 화이트리스트에 있는 경로와 일치하는지 검증합니다.
- 파일 경로가 애플리케이션의 허용된 디렉토리 내에 존재하는지 추가로 확인합니다.
- 이러한 검증 과정을 통과한 경우에만 파일의 내용을 읽어 사용자에게 반환합니다.
위 내용을 요약하자면, "경로 조작 및 자원 삽입" 취약점은 애플리케이션의 보안 메커니즘을 우회하여 비인가된 파일 접근이나 수정을 시도하는 공격입니다. 이러한 공격은 주로 상대 경로를 사용하여 애플리케이션의 루트 디렉토리를 벗어나 중요한 시스템 파일에 접근하는 방식으로 수행됩니다.
어플리케이션을 보호하기 위해, 개발자는 사용자 입력에 대해 특수 기호에 대한 필터링 등 엄격한 입력값 검증을 수행해야 합니다. 또한, File.getCanonicalFile() 메소드 같은 기능을 활용하여 파일 경로의 정규화를 통해 실제 참조되는 파일의 경로를 확인하고, 애플리케이션의 허용된 범위 내에서만 파일 접근을 허용하는 로직을 구현해야 합니다.