보안 공부/소스코드 보안약점 진단

소스코드 보안약점 진단 - 위험한 형식 파일 업로드

H.J.World 2021. 12. 2. 10:10
728x90
반응형

소스코드 보안약점 진단 // 소프트웨어 보안약점 진단 // SW 보안약점 진단과 같이 다양한 이름으로 불리는 진단 과업 중 하나이다.

SW개발보안은 해킹 등 사이버공격의 원인인 보안약점을 SW개발단계에서 사전에 제거하고 SW 개발 생명주기의 각 단계별로 수행하는 일련의 보안활동을 통하여 안전한 SW를 개발·운영하기 위한 목적으로 적용하는 개발체계이다.

해당 내용은 KISA에서 발간하는 취약점 진단 가이드 항목을 기준으로 작성한다.

 

5. 위험한 형식 파일 업로드

가. 개요

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

나. 보안대책

화이트 리스트 방식으로 허용된 확장자만 업로드를 허용한다. 업로드 되는 파일을 저장할 때에는 파일명과 확장자를 외부사용자가 추측할 수 없는 문자열로 변경하여 저장하며, 저장 경로는 ‘web document root’ 밖에 위치시켜서 공격자의 웹을 통한 직접 접근을 차단한다. 또한 파일 실행여부를 설정할 수 있는 경우, 실행 속성을 제거한다.

다. 코드예제

- 안전하지 않은 코드의 예 JAVA -

업로드할 파일에 대한 유효성을 검사하지 않으면, 위험한 유형의 파일을 공격자가 업로드하거나 전송 할 수 있다.

1: MultipartRequest multi 
= new MultipartRequest(request,savePath,sizeLimit,"euc-kr",new DefaultFileRenamePolicy());
2: ......
3: //업로드 되는 파일명을 검증없이 사용하고 있어 안전하지 않다.
4: String fileName = multi.getFilesystemName("filename");
5: ......
6: sql = " INSERT INTO board(email,r_num,w_date,pwd,content,re_step,re_num,filename)
"+ " values ( ?, 0, sysdate(), ?, ?, ?, ?, ? ) ";
7: PreparedStatement pstmt = con.prepareStatement(sql);
8: pstmt.setString(1, stemail);
9: pstmt.setString(2, stpwd);
10: pstmt.setString(3, stcontent);
11: pstmt.setString(4, stre_step);
12: pstmt.setString(5, stre_num);
13: pstmt.setString(6, fileName);
14: pstmt.executeUpdate(); 
15: Thumbnail.create(savePath+"/"+fileName, savePath+"/"+"s_"+fileName, 150);

 

- 안전한 코드의 예 JAVA -

아래 코드는 업로드 파일의 확장자를 검사하여 허용되지 않은 확장자인 경우 업로드를 제한하고 있다.

1: MultipartRequest multi 
= new MultipartRequest(request,savePath,sizeLimit,"euc-kr",new DefaultFileRenamePolicy());
2: ......
3: String fileName = multi.getFilesystemName("filename");
4: if (fileName != null) {
5: //1.업로드 파일의 마지막 “.” 문자열의 기준으로 실제 확장자 여부를 확인하고, 대소문자 구별을
해야한다.
6: String fileExt = FileName.substring(fileName.lastIndexOf(".")+1).toLowerCase();
7: //2.되도록 화이트 리스트 방식으로 허용되는 확장자로 업로드를 제한해야 안전하다.
8: if (!"gif".equals(fileExt) && !"jpg".equals(fileExt) && !"png".equals(fileExt)) {
9: alertMessage("업로드 불가능한 파일입니다.");
10: return;
11: }
12: }
13: ......
14: sql = " INSERT INTO board(email,r_num,w_date,pwd,content,re_step,re_num,filename)
“ + " values ( ?, 0, sysdate(), ?, ?, ?, ?, ? ) ";
15: PreparedStatement pstmt = con.prepareStatement(sql);
16: ......
17: Thumbnail.create(savePath+"/"+fileName, savePath+"/"+"s_"+fileName, 150);

 

- 안전하지 않은 코드의 예  C# -

업로드할 파일에 대한 유효성을 검사하지 않으면, 위험한 유형의 파일을 공격자가 업로드하거나 전송 할 수 있다

1: string fn = Path.GetFileName(FileUploadCtr.FileName);
2: //업로드 하는 파일명을 검증없이 사용하고 있습니다.
3: FileUploadCtr.SaveAs(fn);
4: StatusLabel.Text = "Upload status: File uploaed!";

 

- 안전한 코드의 예 C# -

파일 타입과 크기등을 검사하여 제한하도록 한다.

1: //파일 타입과 크기를 제한합니다.
2: if (FileUploadCtr.PostedFile.ContentType == "image/jpeg")
3: {
4: if (FileUploadCtr.PostedFile.ContentLength < 102400)
5: {
6: string fn = Path.GetFileName(FileUploadCtr.FileName);
7: FileUploadCtr.SaveAs(Server.MapPath("~/") + fn);
8: StatusLabel.Text = "Upload status: File uploaed!";
9: }
10: else
11: StatusLabel.Text = "Upload Status: The File has to be less than 100 kb!";
12: }
13: else
14: StatusLabel.Text = "Upload Status: Only JPEG files are accepted!";

 

라. 진단방법

외부 입력값에서 파일명을 얻어오는 부분이 존재하는지 확인하고(①), 허용된 확장자에 대해서만 파일 업로드를 허용하는지 확인한다(②). 제어문 등을 사용하여 허용된 파일만 업로드 될 경우와 업로 드된 파일명을 외부에서 알 수 없는 형태로 변경할 경우엔 안전하지만 그 외에는 취약하다.

- 일반적인 진단의 예 -

1: public void upload(HttpServletRequest request) throws ServletException {
2: // MultipartHttpServletRequest를 케스팅
3: MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
4:
5: String next = (String) mRequest.getFileNames().next();
6: MultipartFile file = mRequest.getFile(next);
7:
8: // MultipartFile로 부터 file 이름을 얻어옴
9: String fileName = file.getOriginalFilename();-------------①
10:
11: // upload 파일에 대한 확장자 유효성 체크를 하지 않음
12: File uploadDir = new File(“/app/webapp/data/upload/notice”);
13: String uploadFilePath = uploadDir.getAbsolutePath()+”/”+fileName; -- ②
14:
15: /* 이하 file upload 루틴 */
16: }
17: }

 

- 오탐코드의 예 -

다음의 예제는 파일의 확장자를 체크하여 필터링하고 있으므로 안전하다.

1: String orginFileName = file.getOriginalFilename();
2: //--------------------------------------
3: // 원 파일명이 없는 경우 처리
4: // (첨부가 되지 않은 input file type)
5: //--------------------------------------
6: if (“”.equals(orginFileName)) { continue }
7: ////------------------------------------
8: int index = orginFileName.lastIndexOf(“.”);
9: //String fileName = orginFileName.substring(0, index);
10: String fileExt = orginFileName.substring(index + 1); 11: /* 확장자 체크 */
12: for(Object fileExclusionExt : this.fileExclusionExtension) {
13: if( ((String) fileExclusionExt).equals(fileExt.toLowerCase())){
14: throw new Exception(“egume.message.error.file.exclusion.extension”);
15: }
16: }

다음의 예제와 같이 getOriginalFilename()의 리턴 값을 저장하여 사용하지 않으면, 파일명을 사용 하기 위해 다른 곳에서 해당 함수를 호출할 것이므로 취약하지 않다고 판정한다.

1: if (!””.equals(file.getOriginalFilename())) {
2: zipManageService.insertExcelZip(file.getInputStream())
728x90
반응형