본문 바로가기

Web Security/Secure Coding

시큐어 코딩 (Resource Injection)

Resource Injection 공격이란? 

 외부 입력 값을 검증하지 않고 시스템 자원에 대한 식별자로 사용하는 경우, 공격자는 입력 값을 조작하여 시스템이 보호하는 자원에 임의로 접근하거나 수정할 수 있고 잘못된 입력값으로 인해 시스템 자원 사이에 충돌을 불러 일으킬 수 있다. 

 

포트 번호를 사용자로부터 입력받는 경우를 예로 들어보자.

 

[안전하지 않은 코드] 

public void service() throws IOException { 
    int def = 1000; 
    ServerSocket = serverSocket ;
    Properties props = new Properties(); 
    String fildName = "file_list"; 
    FileInputStream in = new FileInputStream(filename); 
    props.load(in) 
    
    // 외부에서 입력한 데이터 받음 
    String service = props.getProperty("Service No"); 
    int port = Integer.parseInt(service); 
    
    // 외부에서 입력받은 값으로 소켓 생성 
    if (port != 0) 
         serverSocket = new ServerSocket(port + 3000); 
    else 
         serverSocket = new ServerSocket(def + 3000); 
    ....
}

위 코드와 같이, 내부자원에 접근할 때 외부 입력 값을 식별자로 직접 사용하는 것은 Resource Injection 을 불러 일으킬 수 있다. 필수적으로 사용되어야 하는 부분이 있다면, 사용 가능한 리스트를 설정한 후 해당 범위 내에서 할당되도록 작성해야한다. 

 

[안전한 코드]

public void service() throws IOException { 
    int def = 1000; 
    ServerSocket = serverSocket ;
    Properties props = new Properties(); 
    String fileName = "file_list"; 
    FileInputStream in = new FileInputStream(filename); 
     
    String service = "";
    if (in != null && in.available() > 0) { 
    	props.load(in) 
        service = props.getProperty("Service No"); 
    }
    
    if ("".equals(service)) service = "8080"; 
    int port = Integer.parseInt(service); 
    
    // Secure Coding (범위 설정) 
    switch(port) { 
         case 1:
             port = 3001; break; 
         case 2:
             port = 3002; break; 
         case 3:
             port = 3003; break; 
         default:
             port = 3000; 
    }
    
    
    // 서버 소켓에 검사완료된 포트 할당 
    serverSocket = new ServerSocket(port + 3000); 
    
    ....
}

다른 예시를 통해, 더 알아보자.

예를 들어, 사용자가 특정 파일을 다운받으려고 한다. 그 때, Resource Injection 공격이 어떻게 발생할 수 있는지 알아보자. 

 

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     response.setContentType("text/html; charset=UTF-8"); 
     response.setStatus(HttpServletResponse.SC_OK); 
     
     String filename = request.getParameter("filename"); 

     File file = new File("./files/" + filename);
     
     if(!file.isFile()) {
         PrintWrite out = response.getWriter(); 
         out.write("<script type='text/javascript'> alert('"+filename+"은 존재하지 않습니다.'); history.back(); </script>");
     }else {
         ...
     }
}

위 코드에서는 필터링 없이 사용자에 의해 입력된 파일명을 통해 다운로드 처리하고 있음을 알 수 있다.

이와 같이, 사용자의 입력에 대해 필터링을 하지 않는다면, 공격자가 다른 디렉토리에 접근하여 다른 파일들을 탈취가 할 수 있게 된다. 

그렇기 때문에, 아래와 같이 몇 가지 필터링 과정을 거쳐줄 필요가 있다. 

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     response.setContentType("text/html; charset=UTF-8"); 
     response.setStatus(HttpServletResponse.SC_OK); 
     
     String rfilename = request.getParameter("filename"); 
     
     // Secure Coding
     int p = rfilename.lastIndexof(".") ; 
     String extension = rfileName.substring(p + 1); 
     String filename = rfilename.substring(0, p); 
     filename = filename.replaceAll("[./&\]",""); // 정규표현식을 이용 
     filename = filename + "." + extension ; 
     
     File file = new File("./files/" + filename);
     
     if(!file.isFile()) { // 파일 없는 경우 
         PrintWrite out = response.getWriter(); 
         out.write("<script type='text/javascript'> alert('"+filename+"은 존재하지 않습니다.'); history.back(); </script>");
     }else {
         ...
     }
}

 

Resource Injection 을 막기위해, replaceAll 메소드에 정규 표현식을 활용하여 몇 가지 특수문자들을 필터링 시켜주었다. 위 코드는 공격을 막기 위한 샘플 코드 일 뿐이므로, 더 안전한 방식을 구현하기 위해서는 추가적인 필터링이 필요하다. 

 

참고 문헌

[1] 시큐어 코딩 보안 가이드 https://www.kisa.or.kr/public/laws/laws3_View.jsp?mode=view

[2] HttpServletRequest https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html

[3] HttpServletResponse https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html