모의침투 조치: 자동화 공격 대응 요청횟수 제한(Rate Limiting) 처리 적용
parent
d61c44f5e4
commit
6b12f9a217
|
|
@ -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 {
|
||||
|
|
@ -197,7 +230,7 @@ public class CommunityController {
|
|||
// String fileName = "";
|
||||
/* for (int i = 1; i < 4; i++) {
|
||||
fileName[i] = multi.getOriginalFileName("fileName" + i);
|
||||
}*/
|
||||
}* /
|
||||
|
||||
FileCmmn fileCmmn = FileCmmn.getInstance();
|
||||
boolean isFileChk = true;
|
||||
|
|
@ -213,6 +246,238 @@ public class CommunityController {
|
|||
return mv;
|
||||
}
|
||||
|
||||
/*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>");
|
||||
} 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>");
|
||||
}
|
||||
/* } catch (IOException e) {
|
||||
System.out.println("e.getMessage() : " + e.getMessage());
|
||||
}* /
|
||||
}
|
||||
|
||||
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>");
|
||||
|
|
@ -290,17 +555,23 @@ public class CommunityController {
|
|||
|
||||
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>");
|
||||
// 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>");
|
||||
// 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 mv;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 게시글 읽기
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
// 파일업로드 확장자 체크 함수
|
||||
|
|
|
|||
Loading…
Reference in New Issue