[백업][가리사니] 서블릿 3.0 / 3.1 의 파일업로드 문제점
java, jsp, servlet

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

Servlet 3.0 / 3.1 에서 파일 업로드가 추가되어 직접 멀티파트를 건들거나 별도의 라이브러리를 쓰지않고도 파일 업로드를 할 수 있지만 아래와 같은 문제점이 발생합니다.

우선 파일업로드의 어노테이션부터 알아보겠습니다.

@MultipartConfig ( fileSizeThreshold = 102410244, // 4MB maxFileSize = 1024102410, // 10MB maxRequestSize = 1024102450, // 50MB location = “D:/WEB_TEMP_FILE” )

maxFileSize

파일 1개당 최대 파일 크기입니다.

maxRequestSize

전체 요청의 크기 크기입니다.

location

기본값은 해당 자바가 실행되는 temp 폴더입니다. (정확히 알수 없다면 정해주는 편이 좋습니다.)

fileSizeThreshold

입력하지 않을 경우 기본값은 0 입니다. 여기서 설정한 용량을 넘어갈 경우 location 에 upload_e1969376_b006_4781_b1cc_006e6e948798_00000074.tmp 같은형태로 저장됩니다.

바로 여기서부터 몇가지 문제점이 발생합니다.

1. maxRequestSize 와 maxFileSize 를 넘어가는경우.

이 두 값을 넘어가는경우 서블릿은 그냥 익셉션을 만들게됩니다. 즉, 파일용량이 조금만 넘어가더라도 서블릿 전체에 오류가 떨어지게됩니다. 물론 클라이언트에서 해당용량을 체크 하겠지만 용량을 클라이언트와 동일하게 잡아두면 위험하기 때문에 조금 더 크게 잡아둡니다. (다만 파일 하나하나의 Part.getSize()로 타이트하게 잡아내셔서 오류를 출력하면됩니다.)

2. location 과 fileSizeThreshold 의 설정문제

fileSizeThreshold 의 기본값은 0 입니다. 즉 0 byte가 넘어가면 temp 파일에 upload_e1969376_b006_4781_b1cc_006e6e948798_00000074.tmp 와 같은 형태로 저장하게됩니다. 즉 fileSizeThreshold = 1024 * 1024 * 4 라고 쓴경우 4mb가 넘지 않는경우 메모리가 기억하며 4mb가 넘는경우 하드디스크의 location에 기록하기 시작합니다. 하지만 location 을 설정해주지 않으면 정확한 temp의 위치를 알 수 없습니다. 사실 여기까지는 큰문제가 되지않지만 Part.delete() 가 제대로 동작하지 않습니다. 즉 이렇게 만들어진 임시파일들이 지워지지않고 차곡차곡 쌓이게 됩니다. 이 파일을 지우는 방법은 2가지가 있습니다. 첫번째는 Part.write(경로)를 사용하여 쓰기를 시도합니다. 이렇게 될경우 해당위치로 파일이동을 하면서 원본파일이 사라지게됩니다. (다만 위 방법을 사용하는경우 이동중 io 익셉션등이 일어나게되면 쓰레기 파일들이 쌓이기 시작합니다.) 두번째는 location 폴더에 가서 만들어진지 하루가 지난 파일 (충분히 사용되지 않는다고 판단되는파일) 들을 수시로 삭제해주는 방법입니다. location 을 지정하지 않은 경우에는 해외 포럼을 찾아보던중 아래와 같은 편법을 사용할 수 있습니다.

File file = File.createTempFile("getter", ".html");

이렇게 경로를 지정하지않는경우 temp폴더에 생성되게 됩니다. 당연한 이야기지만 file에서 해당 경로를 추출할 수 있습니다.

3. 위 방법들 처럼 지저분하게 처리하는게 싫다.!!

@MultipartConfig
(
	location = Conf.PATH_SAVE_FILE_TEMP,		// 공용 파일을 통해 경로를 하나로 관리한다.
	fileSizeThreshold = 1024 * 1024 * 512,		// 512MB : maxRequestSize 보다 높게 설정하여 임시파일을 하드디스크에 기록하지 않도록 만든다.
	maxRequestSize = 1024 * 1024 * 20,			// 20MB : 연결이 끊어져도 될만한 충분한 용량을 설정한다. 다중파일을 받는다면 해당만큼 올려준다.
	maxFileSize = 1024 * 1024 * 12				// 12MB : 10메가 정도 받는다는 생각으로 12메가를 선언했다.
)

위와 같은 설정을 한 이유는 가리사니 같은 경우 램의 용량이 32GB정도 되기 때문에 현재로써 굳이 임시파일에 기록하지 않아도 된다고 판단하였기 때문입니다. 사실 32GB가 부족할 정도로 공격이 들어오는경우 해당 공격을 차단할 방법을 찾는 쪽이 낫다고 생각합니다. 그렇지 않을 경우 하드디스크가 위험할 수 있습니다. location 같은경우는 공용파일을 만들어서 서버를 옴기더라도 한번에 관리할 수 있도록 하였습니다. maxRequestSize, maxFileSize 같은경우는 약간의 여유공간을 두었습니다. 클라이언트에서 용량을 체크할 생각이기 때문에, 전송시 오류가 나느걸 방지하기위해 조금의 여유를 준 것이며, 비상식적으로 더 큰 용량이 오는경우 연결을 끊는 것이 낫다고 생각합니다. 한 아이피당 과도하게 파일을 올리거나 과도하게 짧게 접근하는경우를 차단하는 방식으로 공격을 막는다면 위 방법도 괜찮은 방법이라고 생각됩니다.

추신. 가리사니, 뫼 프레임워크 (파일업로드) 하면서 작성하다보니 끝맺음이 이상하네요…