본문 바로가기

Study/Spring

코드로 배우는 스프링 웹 프로젝트 개정판 파일 업로드 파트 보안 취약점

본 게시물은 코드로 배우는 스프링 웹 프로젝트 개정판 (구멍가게 코딩단) 의 책을 보던중 파일 업로드 관련 부분에서 파일 업로드 확장자를 필터링 하는 부분에 보안 취약점 부분이 발견되서 이를 공유하고 해결방안을 간단하게 작성하고자 합니다.


우선 위의 책을 보셨다는 가정하에 설명을 드리고자 합니다. 

책의 챕터 22를 보면 파일의 확장자와 크기의 제한을 클라이언트 부분에서 javascript를 통해 관리하고 있습니다. 책의 코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
    
    var regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
    var maxSize = 5242880;
    
    function checkExtension(fileName, fileSize){
        if(fileSize >= maxSize){
            alert("파일 사이즈 초과");
            return false;
        }
        
        if(regex.test(fileName)){
            alert("해당 종류의 파일은 업로드 할 수 없습니다.");
            return false;
        }
        return true;
    }
    
    $("#uploadBtn").on("click",function(e){
        var formData = new FormData();
        var inputFile = $("input[name='uploadFile']");
        var files = inputFile[0].files;
        console.log(files);
        
        for(var i=0; i<files.length; i++){
            if(!checkExtension(files[i].name, files[i].size)){
                return false;
            }
            
            formData.append("uploadFile",files[i]);
        }
        
        $.ajax({
            url:'/uploadAjaxAction',
            contentType: false,
            processData: false,
            data : formData,
            type : 'POST',
            success: function(result){
                alert("Uploaded");
            }
        });
        
    });
    
    
});
 
</script>
 
cs



위의 코드는 uploadAjax.jsp에 추가되어 파일의 확장자와 크기를 제한하는데 이런 필터링 과정을 서버 부분이 아닌 클라이언트 부분에서 관리하게되면 간단한 방법으로 회피할 수 있습니다. 


회피하는 방법은 다양한데 저는 프록시 툴을 사용하여 회피하는 과정을 보여드리겠습니다.


우선 프록시 툴을 쓰기전에 프록시가 무엇인지를 알아야 합니다.


https://milkye.tistory.com/202


위의 블로그는 프록시의 개념을 간단히 설명한 글입니다. 이때 프록시 툴이란 간단히 설명하면 프록시 서버의 역활을 해주는 프로그램을 말합니다.


우선 방법을 설명하면 프록시 툴을 사용하여 브라우저와 서버 사이에 프록시 서버가 있는것 처럼 환경을 구성할수 있습니다.



위와 같은 환경을 프록시 툴을 통해 구현하면 이를 통해 브라우저와 서버 사이의 request, response 하는 http 패킷을 프록시 서버( 프록시 툴 ) 을 통해 수정할수 있게됩니다. 따라서 이를 통해 클라이언트로 오는 http 패킷에서 javascript부분을 수정하여 파일 업로드를 제한하는 코드를 제거하는 방법을 통해 파일 업로드 확장자 제한을 회피할 수 있습니다.


프록시 툴의 종류는 다양한데 (brup suite , paros 등..) 저는 brup suite 를 사용하겠습니다.


brup suite의 설치 및 사용법은 구글링을 통해 확인하시기 바랍니다.


이제 책의 챕터 22에 파일의 확장자와 크기의 사전처리 부분을 완료한 뒤 ex05를 톰캣을 통해 실행시킵니다. 그리고 uploadAjax 페이지를 확인하면 다음과 같습니다.


이때 위의 코드가 적용되어 있으므로 zip파일을 업로드 하려하면 다음과 같은 경고창이 나타나면서 업로드가 실패합니다.



여기서 brup suite를 통해 브라우저로 오는 http의 Response 패킷을 확인해보면 다음과 같습니다.


 

프록시 툴을 사용하면 다음과 같이 브라우저로 오는 패킷을 중간에 가로채서 변조가 가능해집니다. 위의 체크하는 부분을 다음과 같이 제거한 뒤 Forward 를 통해 변조된 패킷을 브라우저로 전송합니다.



위의 패킷을 받은 브라우저에서 페이지의 소스를 확인하면 페이지 소스에서 해당 부분이 제거 되었음을 확인 할 수 있습니다.



위의 상태로 브라우저를 통해 파일 업로드를 진행해보면 다음과 같습니다.



javascript를 통해 확장자를 제한하는 부분이 동작하지 못하고 zip파일이 업로드 되는것을 확인할수 있습니다.


위와 같은 보안 취약점은 파일 업로드를 통한 해킹에 노출됩니다. 따라서 위와 같이 보안에 취약할수 있는 부분은 클라이언트 단에서만 관리하는 것이 아니라 서버 단에서도 관리를 해주어야 합니다. 


위의 취약점을 제거하는 방법 중 하나는 서버 단에서 업로드되는 파일의 확장자를 체크하는 방법이 있습니다. 위의 프로젝트에서 UploadController.java에 다음과 같이 코드를 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@PostMapping("/uploadAjaxAction")
    public void uploadAjaxPost(MultipartFile[] uploadFile) {
 
        String uploadFolder = "C:\\upload";
 
        log.info("upload ajax post.......");
 
        for (MultipartFile multipartFile : uploadFile) {
            log.info("------------------");
            log.info("Upload File Name: " + multipartFile.getOriginalFilename());
            log.info("Upload File Size: " + multipartFile.getSize());
 
            String uploadFileName = multipartFile.getOriginalFilename();
 
            // IE has file path
 
            uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\"+ 1);
 
            log.info("only file name: " + uploadFileName);
 
            if (!(uploadFileName.toLowerCase().endsWith(".exe"|| uploadFileName.toLowerCase().endsWith(".sh")
                    || uploadFileName.toLowerCase().endsWith(".zip"|| uploadFileName.toLowerCase().endsWith(".alz"))) {
 
                File saveFile = new File(uploadFolder, uploadFileName);
 
                try {
                    multipartFile.transferTo(saveFile);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
 
            }
            else {
                log.info("제한된 확장자 파일 업로드 시도!");
            }
 
        }
    }
cs


위와 같이 수정한 뒤 같은 방법으로 파일 업로드를 시도 해보았습니다.



다음과 같이 업로드가 되지 않는것을 확인할수 있습니다.


위와 같이 보안에 신경쓰면서 코드를 작성하는 것을 '시큐어 코딩' 이라고 합니다.

그리고 한국인터넷진흥원(KISA)에서는 위와 같은 보안 취약점을 알려주고 시큐어코딩을 하는 가이드를 제작하여 배포하고 있습니다. 


위의 책을 통해서 웹 프로그래밍을 입문하신 분들이 보안과 시큐어 코딩에 관심을 좀더 가졌으면 하는 마음에 게시글을 올립니다. 


아래의 링크는 한국인터넷진흥원(KISA)에서 만든 개발 보안 가이드 pdf 입니다.


http://www.kisa.or.kr/uploadfile/201702/201702140920275581.pdf

'Study > Spring' 카테고리의 다른 글

Spring에 커넥션풀(Connection Pool) 등록 - HikariCP  (2) 2019.01.08