본문 바로가기

Web Security/Secure Coding

시큐어 코딩 (File upload vulnerability)

File Upload Vulnerability 란? 

 서버 측에서 실행될 수 있는 스크립트 파일(asp, jsp, php  파일 등)이 업로드 가능하고, 이 파일을 공격자가 웹을 통해 직접 실행시킬 수 있는 경우 File upload 취약점이 발생할 수 있다. 공격자는 스크립트 파일을 업로드하고 이 파일을 통해 시스템 내부 명령어를 실행하거나 외부와 연결할 경우 시스템을 제어할 수 있게 된다. 

 

File Upload Vulnerability 공격 원리

1. 공격자가 스크립트 파일을 웹 사이트에 업로드 

2. 공격자가 해당 스크립트 파일 (php, jsp, asp, cgi, inc, pl, php3 등) 이 저장된 경로에 접근이 가능할 경우, 업로드 된 경로로 이동

3. 해당 파일에 접근하여 파일을 실행

4. 웹 쉘 실행

 

파일 업로드 취약점에서 자주 사용하는 우회 방식 

[이미지 파일 형식만 업로드 가능한 경우] 

1. hack.php 형식으로 파일 업로드 버튼을 누른 후 Burp Suite Tool 을 사용하여 중간에서 가로 챈 후, Content-Type 을 image/jpeg 로 바꿔준다.

2. 이름의 확장자만 확인할 경우, hack.php%00.jpg 이와 같이 적어준 후, Burp Suite Tool 로 hack.php로 수정하여 업로드한다.

3. 이미지 파일에다가 Reverse 쉘 코드를 Redirect 를 통해 삽입하여 업로드 한다. 

 

이 때, 파일에 접근이 어려울 경우, File Inclusion 취약점이 발생하는 곳을 찾아주어 공격할 때도 있다. 

 

[안전하지 않은 코드]

public void upload(HttpServletRequest request) throws ServletException { 
    MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request ;
    String next = (String)mRequest.getFileNames().next(); 
    MultipartFile file = mRequest.getFile(next); 
    
    // MultipartFile로부터 file을 얻음
    String filename = file.getOriginalFilename(); 
    
    File uploadDir = new File("파일 경로"); 
    String uploadFilePath = uploadDir.getAbsoultePath() + "/" + filename; 
    
    /* 이하 파일 업로드 루틴 */ 
}

위 코드를 보면, 어떠한 파일 타입 및 확장자, 크기 등을 확인하지 않고 파일 경로를 형성하는 것을 볼 수 있다. 이러한 경우 File upload 취약점이 발생할 수 있다. 이를 기본적으로 해결하기 위해서는 타입 및 확장자, 파일 크기 또한 확인해주는 과정을 거쳐줘야한다. 

 

[안전한 코드] 

public void upload(HttpServletRequest request) throws ServletException { 
    MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request; 
    String next = mRequest.getFileNames().next(); 
    MultipartFile file = mRequest.getFile(next); 
    
    if (file == null) { 
        return ;
    }
    
    // Secure Coding (업로드 파일 크기 제한)
    int size =  file.getSize(); 
    if( size > MAX_FILE_SIZE) throw new ServletException("에러"); 
    
    String filename = file.getOriginalFilename().toLowercase(); 
    
    if( filename != null) { 
        // Secure Coding (화이트 리스트 방식으로 확장자 체크) 
        if(fileName.endsWith(".doc") || fileName.endsWith(".hwp") || fileName.endsWith(".pdf") || fileName.endsWith(".xls")) {
            // file upload 
            ...
            
        }else {
            throw new ServletExpection("에러"); 
        }
    }
    // Secure Coding (업로드 파일의 디렉터리 위치는 다큐먼트 루트의 밖에 위치 시킴)
    File uploadDir = new File("/app/webapp/data/upload/notice"); 
    String uploadFilePath = upload.getAbsolutePath() + "/" + fileName ;
    
}

위 코드는 업로드 파일 크기 제한, 화이트 리스트 방식으로 확장자 체크, 업로드 파일의 디렉터리 위치를 루트의 밖에 위치 시킴으로써 보안 코드를 작성하였다. 이것 외에도 아래와 같이 파일 내용을 기반으로 file 의 type 을 판별하는 코드 또한 필요하다. 

if(extractFileType(file) == EXECUTEABLE_FILE_TYPE){ 
    // error 
    return ;
}

만일, 파일 타입이 실행 가능한 파일일 경우 에러를 발생시켜 해당 파일이 업로드 되는 것을 막아준다. 이러한 보안 기법들을 사용하더라도 조금 더 보안을 높이기 위해선 파일 명을 암호화 알고리즘을 사용하여 저장하는 방식 또한 보안 수준을 높여줄 수 있다. 

 

아래 코드는 md5 암호화 알고리즘 코드이기에, 참고하면 좋을 것 같다. 

[md5 암호화 알고리즘] 

//Create checksum for this file
File file = new File("c:/temp/testOut.txt");
 
//Use MD5 algorithm
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
 
//Get the checksum
String checksum = getFileChecksum(md5Digest, file);

 

보안 방법

 해당 파일이 "image/jpeg" 혹은 "image/jpg" 인지 타입 및 확장자를 확인한 후, 파일 크기 또한 확인해준다. 더 나아가, 파일 명을 암호화 알고리즘을 사용하여 저장해주면 해당 파일의 이름에 접근이 어렵기 때문에 보안 수준을 높일 수 있다. 추가적으로, 업로드 경로에 접근을 불가능하도록 제한을 걸어주거나 업로드 디렉터리를 웹 서버의 다큐먼트 외부에 설정해놓으면 된다. 

 

 

참고 문헌

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

[2] MD5 https://howtodoinjava.com/java/io/sha-md5-file-checksum-hash/