모의침투 조치: 자동화 공격 대응 요청횟수 제한(Rate Limiting) 처리 적용

main
유지인 2026-06-17 19:03:58 +09:00
parent d61c44f5e4
commit 6b12f9a217
2 changed files with 301 additions and 7 deletions

View File

@ -14,16 +14,17 @@ import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.jfree.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.ModelAndView;
@ -106,6 +107,38 @@ public class CommunityController {
return mv;
}
/**
* (260616/YJI)
* 15
* @param request
* @param response
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/rateLimitTst.do")
public Map<String, Object> cmuboard_save_rateLimit(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
// (260615/YJI) 사이버모의침투 대응훈련 자동화 공격 관련 조치 시작
HttpSession session = request.getSession();
// 동일한 작성자의 15초 이내로 게시글 등록을 막는다
Long lastWriteTime = (Long) session.getAttribute("BOARD_WRITE_TIME");
long now = System.currentTimeMillis();
if(lastWriteTime != null && now - lastWriteTime < 15000) {
result.put("code", "FAIL");
result.put("msg", "연속된 글 등록은 제한됩니다. 잠시 후 다시 시도해 주세요.");
return result;
}
// (260615/YJI) 사이버모의침투 대응훈련 자동화 공격 관련 조치 끝
session.setAttribute("BOARD_WRITE_TIME", now); // (260615/YJI) 사이버모의침투 대응훈련 자동화 공격 관련 조치
result.put("code", "SUCCESS");
return result;
}
/*
// 게시글 저장
@RequestMapping(value = "/cmuboard_save.do")
public ModelAndView cmuboard_save(MultipartRequest multi, HttpServletRequest request, HttpServletResponse response, Map<String, Object> map) throws Exception {
@ -301,6 +334,244 @@ public class CommunityController {
}
return mv;
}*/
/**
* (260616/YJI)
* 15
* @param multi
* @param request
* @param response
* @param map
* @return code, msg
* SUCCESS: , FAIL: , RATE_LIMIT: , LOGIN_EXP: , BLOCKED_WORD: , INVALID_FILE:
* BLOCKED_WORD
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/cmuboard_save.do")
public Map<String, Object> cmuboard_save(MultipartRequest multi, HttpServletRequest request, HttpServletResponse response, Map<String, Object> map) throws Exception {
// ModelAndView mv = new ModelAndView("body/cmuboard/cmuboard_save");
Map<String, Object> result = new HashMap<String, Object>();
// (260615/YJI) 사이버모의침투 대응훈련 자동화 공격 관련 조치 시작
HttpSession session = request.getSession();
// 동일한 작성자의 15초 이내로 게시글 등록을 막는다
Long lastWriteTime = (Long) session.getAttribute("BOARD_WRITE_TIME");
long now = System.currentTimeMillis();
if(lastWriteTime != null && now - lastWriteTime < 15000) {
result.put("code", "RATE_LIMIT");
result.put("msg", "연속된 글 등록은 제한됩니다. 잠시 후 다시 시도해 주세요.");
return result;
}
// (260615/YJI) 사이버모의침투 대응훈련 자동화 공격 관련 조치 끝
if (request.getSession().getAttribute("USERID") == null) {
//mv.addObject("msg", "<script>alert('로그인후 등록하실 수 있습니다');window.location.href='cmuboard.do?page=0';</script>"); // 202007 삭제
// mv.addObject("msg", "<script>alert('로그인후 등록하실 수 있습니다');window.location.href='topMenuSelect.do?url=cmuboard';</script>");
// return mv;
result.put("code", "LOGIN_EXP");
result.put("msg", "로그인후 등록하실 수 있습니다");
return result;
} else {
String savePath = EgovProperties.getProperty("Geoinfo.FilePath");
String fileName[] = new String[4];
String saveName[] = new String[4];
int pos = 1;
Map<String, MultipartFile> multipartFiles = multi.getFileMap();
for(Entry<String, MultipartFile> entry : multipartFiles.entrySet()) {
MultipartFile multipartFile = entry.getValue();
if(!multipartFile.isEmpty()) {
fileName[pos] = new String(multipartFile.getOriginalFilename().getBytes());
// fileName333[pos] = new String(multipartFile.getOriginalFilename().getBytes("8859_1"),"euc-kr");
// fileName[pos] = new String(multipartFile.getOriginalFilename().getBytes("8859_1"),"utf-8");
System.out.println(entry.getKey() + " : " + fileName[pos]);
System.out.println("savePath = " + savePath);
//웹 취약점 때문에 수정
String file_ext = fileName[pos].substring(fileName[pos].lastIndexOf('.') + 1); // 파일확장자
String file_name = fileName[pos].substring(0,fileName[pos].lastIndexOf('.')); // 파일확장자
file_ext = file_ext.replaceAll("\\.", "").replaceAll("/", "").replaceAll("\\\\", "").replaceAll ("&","");
file_name = file_name.replaceAll("\\.", "").replaceAll("/", "").replaceAll("\\\\", "").replaceAll ("&","");
//웹 취약점 때문에 수정 23.02.14
String new_file = (savePath + file_name + "." + file_ext);
System.out.println(new_file);
File file = new File(new_file);
if(!file.isFile()) {
file.createNewFile();
}
OutputStream output = new FileOutputStream(file);
IOUtils.copy(multipartFile.getInputStream(), output);
output.close();
pos++;
}
}
//MultipartRequest multi = null;
int fileSizeLimit = 500 * 1024 * 1024;
//try {
//multi = new MultipartRequest(request, savePath, fileSizeLimit, "euc-kr", new DefaultFileRenamePolicy());
String name = (String)request.getSession().getAttribute("USERNAME");
// String name = multi.getParameter("name");
String password = request.getParameter("password");
String email = request.getParameter("email");
String homepage = request.getParameter("homepage");
String subject = request.getParameter("subject");
String content = request.getParameter("content");
// 금칙어 검증 메소드 호출
String detected = MyUtil.checkForbiddenWords(content);
if (!detected.isEmpty()) {
// String alertMsg = "운영에 허용되지 않는 단어로 인해 게시글 등록이 실패하였습니다.\\n차단된 단어: " + detected;
// mv.addObject("msg", "<script>alert('" + alertMsg + "');history.go(-1);</script>");
// return mv;
result.put("code", "BLOCKED_WORD");
result.put("msg", "운영에 허용되지 않는 단어로 인해 게시글 등록이 실패하였습니다.\\n차단된 단어: " + detected);
return result;
}
//String subject = new String(request.getParameter("subject").getBytes("8859_1"),"utf-8");
//String content = new String(request.getParameter("content").getBytes("8859_1"),"utf-8");
name = GeoinfoCommon.strReplace(name);
password = GeoinfoCommon.strReplace(password);
email = GeoinfoCommon.strReplace(email);
subject = GeoinfoCommon.strReplace(subject);
// 특수문자열 처리(HTML태그)
email = GeoinfoCommon.parseData(email);
homepage = GeoinfoCommon.parseData(homepage);
subject = GeoinfoCommon.parseData(subject);
content = GeoinfoCommon.parseData(content);
int affectedRows = 0;
String L_dat = null;
String L_tmp = null;
// String saveName = "";
// String fileName = "";
/* for (int i = 1; i < 4; i++) {
fileName[i] = multi.getOriginalFileName("fileName" + i);
}*/
FileCmmn fileCmmn = FileCmmn.getInstance();
boolean isFileChk = true;
for (int i = 1; i < 4; i++) {
if (fileName[i] != null) {
String file_ext = fileName[i].substring(fileName[i].lastIndexOf('.') + 1);
if ( ! fileCmmn.isZipCheck(fileName[i])) {
//mv.addObject("msg", "<script>alert('등록할 수 없는 파일입니다.');window.location.href='cmuboard.do?page=0';</script>"); // 202007 삭제
// mv.addObject("msg", "<script>alert('등록할 수 없는 파일입니다.');window.location.href='topMenuSelect.do?url=cmuboard';</script>");
// fileName[i] = null;
// return mv;
result.put("code", "INVALID_FILE");
result.put("msg", "등록할 수 없는 파일입니다.");
return result;
}
/*if (!(file_ext.equalsIgnoreCase("hwp") || file_ext.equalsIgnoreCase("pdf") || file_ext.equalsIgnoreCase("ppt") || file_ext.equalsIgnoreCase("pptx") || file_ext.equalsIgnoreCase("wmv") || file_ext.equalsIgnoreCase("txt") || file_ext.equalsIgnoreCase("exe") || file_ext.equalsIgnoreCase("zip") || file_ext.equalsIgnoreCase("jpg") || file_ext.equalsIgnoreCase("gif") || file_ext.equalsIgnoreCase("png") || file_ext.equalsIgnoreCase("doc") || file_ext.equalsIgnoreCase("docx")
|| file_ext.equalsIgnoreCase("xlsx") || file_ext.equalsIgnoreCase("xls"))) {
mv.addObject("msg", "<script>alert('등록할 수 없는 파일입니다.');window.location.href='cmuboard.do?page=0';</script>");
fileName[i] = null;
for (int j = 1; j < 4; j++) {
if (multi.getFile("fileName" + j) != null)
System.out.println("AAAAA" + j);
//multi.getFile("fileName" + j)..delete();
}
return mv;
}*/
}
}
try {
for (int i = 1; i < 4; i++) {
// fileName[i] =
// multi.getOriginalFileName("fileName"+i);
System.out.println("fileName = " + fileName[i]);
if (fileName[i] != null) {
GregorianCalendar cal = new GregorianCalendar();
L_dat = cal.get(Calendar.YEAR) + "_" + cal.get(Calendar.MONTH) + "_" + cal.get(Calendar.DATE);
L_tmp = cal.get(Calendar.HOUR) + "_" + cal.get(Calendar.MINUTE) + "_" + cal.get(Calendar.SECOND);
saveName[i] = L_dat + "_" + L_tmp + i + fileName[i].substring(fileName[i].lastIndexOf("."));
} else {
fileName[i] = "";
saveName[i] = "";
}
File up1 = new File(savePath + "/", FilenameUtils.getName(fileName[i]));
File up2 = new File(savePath + "/", FilenameUtils.getName(saveName[i]));
if (up1.exists()) {
boolean rslt = up1.renameTo(up2);
}
}
String fileName1 = fileName[1];
String fileName2 = fileName[2];
String fileName3 = fileName[3];
String saveName1 = saveName[1];
String saveName2 = saveName[2];
String saveName3 = saveName[3];
map.put("name", name);
map.put("pass", password);
map.put("email", email);
map.put("homepage", homepage);
// 2017.10.18 dhlee 스크립트 및 특수문자 변환
subject = RequestWrapper.cleanXSS(subject);
map.put("subject", subject);
// 2017.10.18 dhlee 스크립트 및 특수문자 변환
content = RequestWrapper.cleanXSS(content);
map.put("content", content);
map.put("fileName", fileName1);
map.put("saveName", saveName1);
map.put("fileName2", fileName2);
map.put("saveName2", saveName2);
map.put("fileName3", fileName3);
map.put("saveName3", saveName3);
map.put("top", 0);
communityService.insertCmuboard(map);
affectedRows = 1;
} catch (IndexOutOfBoundsException ex) {
logger.debug("error", ex);
} catch (NumberFormatException ex) {
logger.debug("error", ex);
} catch (IOException ex) {
logger.debug("error", ex);
} catch (Exception e) {
logger.debug("error", e);
}
if (affectedRows > 0) {
//mv.addObject("msg", "<script>alert('정상적으로 등록이 완료되었습니다.');window.location.href='cmuboard.do?page=0';</script>"); // 202007 삭제
// mv.addObject("msg", "<script>alert('정상적으로 등록이 완료되었습니다.');window.location.href='topMenuSelect.do?url=cmuboard';</script>");
session.setAttribute("BOARD_WRITE_TIME", now); // (260615/YJI) 사이버모의침투 대응훈련 자동화 공격 관련 조치
result.put("code", "SUCCESS");
result.put("msg", "정상적으로 등록이 완료되었습니다.");
} else {
//mv.addObject("msg", "<script>alert('오류입니다.');window.location.href='cmuboard.do?page=0';</script>"); // 202007 삭제
// mv.addObject("msg", "<script>alert('오류입니다.');window.location.href='topMenuSelect.do?url=cmuboard';</script>");
result.put("code", "FAIL");
result.put("msg", "오류입니다.");
}
/* } catch (IOException e) {
System.out.println("e.getMessage() : " + e.getMessage());
}*/
}
// return mv;
return result;
}
// 게시글 읽기

View File

@ -169,7 +169,30 @@
}
}
f.submit();
var formData = new FormData(f);
$.ajax({
url : "cmuboard_save.do",
type : "POST",
data : formData,
processData : false,
contentType : false,
cache : false,
success : function(res) {
if(res.code == "SUCCESS") {
alert(res.msg);
location.href = "topMenuSelect.do?url=cmuboard";
} else {
alert(res.msg);
}
},
error : function(xhr, status, error) {
alert("처리 중 오류가 발생하였습니다.");
console.log(error);
}
});
}
}
// 파일업로드 확장자 체크 함수