본문 바로가기
Backend/Spring

Spring boot에서 첨부파일 업로드 (Apache Tika 파일 변조 체크)

by 나는 유찌 2021. 11. 14.

어느 날 대충 프로젝트 마무리가 지어지고 딩가딩가 놀고 있던 중 고객사에서 메일이 하나 날아왔다.

불길한 기운이 스멀스멀...

메일함을 열고 확인을 해보자 jsp 파일을 이름 변경하여 txt 형식으로 바꾸고 업로드하면 그대로 업로드가 된다는 내용이었다.

 

그게 모에영..??

 

당시 경력 1년이 조금 넘은 나는 이런 내용이 있었구나 하며 뚱땅뚱땅 수정 작업을 하였고 이를 한참이나 지난 지금에야... 블로그에 정리를 하여 업로드를 한다ㅎㅎ..

 

Spring boot를 사용하였고 템플릿 엔진은 thymeleaf을 사용하였다.

따로 DB Connect는 하지 않았으며 파일 변조 체크 로직만 작성해보았다.

 

 

 

1단계 - 자바스크립트 이용


<!DOCTYPE html>
<html lang="en">
<body>
  <h1>fileUpload</h1>
  <form name="fileForm" th:action="@{/file-upload}" method="post" enctype="multipart/form-data">
    <input type="file" name="file" value="첨부파일"/>
    <br>
  </form>
  <input type="button" value="업로드" onclick="document.fileForm.submit()"/>

  <script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
  <script>
    $("input[name=file]").change(function () {
      var fileExtension = ['jpeg', 'jpg', 'png', 'gif', 'bmp'];
      if ($.inArray($(this).val().split('.').pop().toLowerCase(), fileExtension) == -1) {
        alert("이미지 파일만 업로드 가능합니다. (" + fileExtension.join(", ") + ")");
        $("input[name=file]").val("");
      }
    });
  </script>
</body>
</html>

 

이렇게 html을 작성한 후 실행을 하고 jpeg, jpg, png 등 이미지 확장자가 아닌 경우

 

다음과 같은 alert이 나오게 된다.

 

 

 

2단계 - Apache Tika 사용


html 파일 하나를 생성하고 파일 이름을 변경하여 확장자를 png로 변경을 한다.

그리고 파일 업로드를 진행하면 1단계에서 작성한 자바스크립트에서 png 이미지 파일이라 인식을 하여 업로드가 정상적으로 진행이 된다.

 

만일 악의적으로 스크립트를 작성하여 파일을 업로드하게 된다면 권한을 가지게 된다는 등 공격을 당할 수 있다고 한다.

 

이를 어떤 식으로 예방하면 되는지 아래 코드를 확인해보자.

업로드한 파일은 'test.html'로 처음 생성하여 'test.png'로 이름을 바꿔주었다.

 

 

<dependency>
	<groupId>org.apache.tika</groupId>
	<artifactId>tika-core</artifactId>
	<version>1.24.1</version>
</dependency>

tika dependency를 위와 같이 추가해주고 maven update를 진행해준다.

 

@PostMapping("/file-upload")
public ResponseEntity fileUpload(@RequestParam("file")MultipartFile uploadFile) {
        fileService.fileUpload(uploadFile);
        return ResponseEntity.ok().build();
}

 

Controller에서 MultipartFile로 파일을 받고 Service로 넘겨준다.

 

@Service
public class FileServiceImpl implements FileService {

    @Override
    public void fileUpload(MultipartFile uploadFile) {
        try(InputStream inputStream = uploadFile.getInputStream()) {
            System.out.println("Content Type : " + uploadFile.getContentType());

            if(!uploadFile.isEmpty()) {
                boolean isValid = FileUtils.validImgFile(inputStream);
                if(!isValid) {
                    // exception 처리
                    System.out.println("이미지 파일만 업로드 가능합니다.");
                }
                // 업로드 로직
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileUtils라는 class 파일을 만들고 거기다 확장자 체크 기능을 만들어주었다.

 

public class FileUtils {

    private static final Tika tika = new Tika();

    public static boolean validImgFile(InputStream inputStream) {
        try {
            List<String> notValidTypeList = Arrays.asList("image/jpeg", "image/pjpeg", "image/png", "image/gif", "image/bmp", "image/x-windows-bmp");

            String mimeType = tika.detect(inputStream);
            System.out.println("MimeType : " + mimeType);

            boolean isValid = notValidTypeList.stream().anyMatch(notValidType -> notValidType.equalsIgnoreCase(mimeType));

            return isValid;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
}

FileUtils에 tika를 이용하여 mime type을 체크해준다.

개발자가 알고 있는 파일 확장자보다 정말 많은 파일 확장자들이 있다.

그래서 확장자를 체크할 때는 업로드 할 수 없는 확장자를 체크하는 것보다 업로드가 가능한 파일의 확장자를 체크하는 것이 더 좋다.

 

위의 코드 또한 notValidTypeList로 (변수명은 white라는 단어를 사용하는 것이 더 좋을듯하다) 이미지 파일의 mime type을 선언해주었다.

 

여러 확장자의 mime type이 어떤 것인지는 아래 사이트를 참고하였다.

https://www.htmlstrip.com/mime-file-type-checker

 

MIME File Type Checker - HTMLStrip

MIME File Type Checker A handy tool to check what type of file you are dealing with by checking the Multipurpose Internet Mail Extension. This is especially useful for developers who are working on validation and want to know if the file MIME type being te

www.htmlstrip.com

 

 

 

3단계 - 테스트


html 파일을 하나 만들어주고 파일의 이름을 변경하여 확장자 부분을 png로 바꿨다.

 

위의 파일을 업로드하면 Consol에 위와 같이 출력이 된다.

MultipartFile에서 getContentType을 하면 'image/png'로 출력이 되는 것을 확인할 수 있다.

하지만 Mime Type은 'text/html'이라고 나오는 것을 확인할 수 있고 이후로는 Exception 처리를 하면 되겠다.

 

 

 

해당 코드는 아래 깃 주소에 올려두었다.

https://github.com/yujin-Kim-98/apache-tika

 

GitHub - yujin-Kim-98/apache-tika: apache-tika

apache-tika. Contribute to yujin-Kim-98/apache-tika development by creating an account on GitHub.

github.com

 

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

Spring boot에서 JWT 구현(2)  (3) 2022.02.20
Spring boot에서 JWT 구현(1)  (0) 2022.02.19
Spring boot에 lucy-xss-servlet-filter 적용하기  (0) 2021.07.11
[Spring boot] Swagger  (0) 2020.05.20
[Spring boot] 다중 데이터베이스 연결  (6) 2020.03.14