[ Java ] File Upload Vulnerability
포스트
취소

[ Java ] File Upload Vulnerability

취약한 코드


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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package kr.co.openeg.lab.test.controller;

import java.io.File;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import kr.co.openeg.lab.test.model.BoardModel;
import kr.co.openeg.lab.test.service.BoardService;
import kr.co.openeg.lab.test.util.ConvertInput;

public class BoardController {

	@Autowired
	private BoardService service;

	@RequestMapping(value="write", method=RequestMethod.GET)
	public ModelAndView boardWrite(ModelAndView mav, HttpSession session) {

		mav.setViewName("board/write");
		return mav;
	}

	@RequestMapping(value="write", method=RequestMethod.POST)
	public ModelAndView boardWriteProc(ModelAndView mav, @ModelAttribute("BoardModel") BoardModel boardModel,
			MultipartHttpServletRequest request, HttpSession session) throws ServletException {

		try {

			MultipartFile file = request.getFile("file");

			if (file != null && !"".equals(file.getOriginalFilename())) {

				String fileName = file.getOriginalFilename();

				if (fileName != null) {

					String uploadPath = System.getProperty("user.dir") + "/files/";
					File uploadFile = new File(uploadPath + fileName);

					try {
						file.transferTo(uploadFile);
					} catch (Exception e) {
						mav.addObject("msg", false);
						mav.addObject("fuploaderror", false);
						throw new ServletException("파일 업로드 에러");
					}
				} else {
					mav.addObject("msg", false);
					mav.addObject("uploaderror", false);
					throw new ServletException("파일 확장자 에러");
				}
				boardModel.setFileName(fileName);
			}

			String title = request.getParameter("title");
			boardModel.setTitle(title);

			String content = boardModel.getContent().replaceAll("\r\n", "<br />");
			String convertedContent = ConvertInput.ConvertInputValue(content);
			boardModel.setContent(convertedContent);

			String writerId = request.getParameter("writerId");
			boardModel.setWriter(writerId);

			if (service.writeArticle(boardModel)) {
				mav.setViewName("redirect:/");
				return mav;
			}
		} catch (ServletException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

		mav.setViewName("board/write");
		return mav;
	}
} 



취약점 분석


1
file.transferTo(uploadFile);

사용자가 업로드한 파일을 아무런 검사 없이 서버에 저장하고 있다.

이런 경우 공격자는 악의적인 파일을 업로드하고 실행시킬 수 있다.


업로드 시 입력된 파일이 허용되는 형식인지 확인하여 해당 취약점을 방지할 수 있다.

또한 파일의 사이즈를 제한하고, 파일명을 추측할 수 없게 하는 방법이 있다.



안전한 코드


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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package kr.co.openeg.lab.test.controller;

import java.io.File;
import java.util.Date;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import kr.co.openeg.lab.test.model.BoardModel;
import kr.co.openeg.lab.test.service.BoardService;
import kr.co.openeg.lab.test.util.ConvertInput;

public class SecureBoardController {

	@Autowired
	private BoardService service;
	private int MAX_FILE_SIZE = 1024 * 1024 * 5;

	@RequestMapping(value = "write", method = RequestMethod.GET)
	public ModelAndView boardWrite(ModelAndView mav, HttpSession session) {

		mav.setViewName("board/write");
		return mav;
	}

	@RequestMapping(value = "write", method = RequestMethod.POST)
	public ModelAndView boardWriteProc(ModelAndView mav, @ModelAttribute("BoardModel") BoardModel boardModel,
			MultipartHttpServletRequest request, HttpSession session) throws ServletException {

		try {

			MultipartFile file = request.getFile("file");

			if (file != null && !"".equals(file.getOriginalFilename())) {

				/* 파일사이즈 제한을 위해 첨부된 파일의 사이즈를 가져오는 메소드와 파일 최대사이즈 변수를 사용*/
				int fileSize = (int) file.getSize();
				if (fileSize > MAX_FILE_SIZE) {
					mav.addObject("msg", false);
					mav.addObject("fsizeerror", false);
					throw new ServletException("파일 용량 제한 에러");
				}

				String fileName = file.getOriginalFilename();
				String savedFileName = null;

				if (fileName != null) {
					/* pdf파일만 업로드 가능하도록 제약 설정 */
					if (fileName.toLowerCase().endsWith(".pdf")) {

						String uploadPath = System.getProperty("user.dir") + "/files/";

						/** 저장하는 파일명은 랜덤하게 생성해서 사용하도록 코드 작성 */
						savedFileName = UUID.randomUUID().toString();
						File uploadFile = new File(uploadPath + savedFileName);

						try {
							file.transferTo(uploadFile);
						} catch (Exception e) {
							mav.addObject("msg", false);
							mav.addObject("fuploaderror", false);
							throw new ServletException("파일 업로드 에러");
						}
					} else {
						mav.addObject("msg", false);
						mav.addObject("uploaderror", false);
						throw new ServletException("파일 확장자 에러");
					}
				}
				boardModel.setFileName(fileName);
				boardModel.setSavedFileName(savedFileName);
			}

			String title = request.getParameter("title");
			boardModel.setTitle(title);

			String content = boardModel.getContent().replaceAll("\r\n", "<br />");
			String convertedContent = ConvertInput.ConvertInputValue(content);
			boardModel.setContent(convertedContent);

			String writerId = request.getParameter("writerId");
			boardModel.setWriter(writerId);

			if (service.writeArticle(boardModel)) {
				mav.setViewName("redirect:/");
				return mav;
			}
		} catch (ServletException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

		mav.setViewName("board/write");
		return mav;
	}
}