관리번호 | |||
취약점명 | 중요정보(phpinfo, editor, 세부 버전정보 등) 외부 노출 | ||
점검일자 | 2024.08.19. ~ 2024.08.23. | ||
위험도 | 하 | 조치 시급성 | 단기 |
취약위치 | |||
경로 | - | ||
URL 1) | resources/editor/SmartEditor2/SmartEditor2.html | ||
URL 2) | resources/editor/SmartEditor2/SmartEditor2Skin.html | ||
파라미터 | - |
[선택 방법]
Apache Tomcat 서버에서 특정 경로나 파일에 대한 접근을 제한하는 방법은 여러 가지가 있습니다. Tomcat의 경우, 기본적으로 web.xml 또는 Tomcat 설정 파일을 수정하여 특정 URL이나 리소스에 대한 접근을 제어할 수 있습니다. 아래에 Tomcat에서 특정 파일에 대한 접근을 막는 여러 방법을 설명합니다.
1. web.xml을 사용한 접근 제한
Tomcat 서버에서는 web.xml을 사용하여 특정 경로나 파일에 대한 접근을 제어할 수 있습니다. 이 파일은 애플리케이션의 설정 파일로, 접근 제어와 같은 여러 설정을 정의할 수 있습니다.
/WEB-INF/web.xml 파일에 다음 내용을 추가하여 특정 경로에 대한 접근을 차단할 수 있습니다.
<security-constraint>
<web-resource-collection>
<web-resource-name>SmartEditor2 restriction</web-resource-name>
<url-pattern>/resources/editor/SmartEditor2/SmartEditor2.html</url-pattern>
<url-pattern>/resources/editor/SmartEditor2/SmartEditor2Skin.html</url-pattern>
</web-resource-collection>
<auth-constraint>
<!-- 역할을 지정하지 않음으로써 모든 사용자에게 차단 -->
</auth-constraint>
</security-constraint>
위 설정은 /resources/editor/SmartEditor2/SmartEditor2.html 경로에 대한 접근을 차단합니다. auth-constraint를 빈 값으로 설정하면 인증이 요구되지 않는 모든 사용자에 대해 접근이 차단됩니다.
고려할 수 있는 방법
2. Tomcat의 server.xml을 수정하여 접근 제한
Tomcat의 server.xml을 수정하여 특정 경로에 대한 접근을 막는 방법도 있습니다. Tomcat의 server.xml 파일에 Valve를 추가하여 특정 경로에 대한 요청을 필터링할 수 있습니다.
- Tomcat의 conf/server.xml 파일을 엽니다.
- Host 태그 내부에 다음과 같은 Valve 설정을 추가합니다.
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
deny="^.*$"
allow="" />
<!-- Other configurations -->
</Host>
이렇게 하면 해당 경로로 접근하는 모든 사용자를 차단할 수 있습니다. 특정 경로를 제어하려면 deny나 allow 규칙을 설정할 수 있습니다.
3. Tomcat의 context.xml에서 리소스 접근 제어
context.xml 파일에서도 특정 파일이나 디렉터리에 대한 접근을 제어할 수 있습니다. context.xml 파일에 Resources 요소를 추가하여 설정합니다.
<Context>
<Resources>
<PreResources className="org.apache.catalina.webresources.DirResourceSet"
webAppMount="/resources/editor/SmartEditor2/"
base="path/to/your/tomcat/webapps/resources/editor/SmartEditor2"
readOnly="false" />
</Resources>
</Context>
이 설정을 통해 해당 디렉터리에 대한 접근 권한을 제어할 수 있습니다. readOnly="false"로 설정하면 해당 디렉터리에 대한 읽기 접근이 불가능하게 됩니다.
4. Tomcat URL 필터 설정
Tomcat에서 URL 필터를 설정하여 특정 경로에 대한 접근을 제어할 수도 있습니다. web.xml에 필터를 추가하여 요청을 제어하는 방식입니다.
- web.xml에 필터 설정 추가
<filter>
<filter-name>URLFilter</filter-name>
<filter-class>com.example.URLFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>URLFilter</filter-name>
<url-pattern>/resources/editor/SmartEditor2/*</url-pattern>
</filter-mapping>
- 필터 클래스 작성
필터 클래스를 작성하여 특정 URL에 대한 접근을 필터링합니다.
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class URLFilter implements Filter {
public void init(FilterConfig filterConfig) {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
if (requestURI.contains("/resources/editor/SmartEditor2/SmartEditor2.html")) {
// 접근 차단
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN);
} else {
chain.doFilter(request, response); // 다른 요청은 정상 처리
}
}
public void destroy() {}
}
이 방법을 통해 특정 URL에 대한 접근을 프로그램적으로 제어할 수 있습니다.
관리번호 | MW_LNJ_2024_212 | ||
취약점명 | 자동화 공격 | ||
점검일자 | 2024.08.19. ~ 2024.08.23. | ||
위험도 | 하 | 조치 시급성 | 단기 |
취약위치 | |||
경로 | 홈 > 알림마당 > 안전제안 > 아차사고 신고/안전 제안 | ||
URL | /home/kor/notice/safety/write.do | ||
파라미터 | - | ||
취약점 설명 | |||
게시글 등록 시 자동화 공격을 통해 다수의 게시글 등록 가능 |
자동화 공격을 방지하기 위해 CSRF(Cross-Site Request Forgery) 토큰을 적용하는 방식으로 게시글 등록을 보호할 수 있습니다. 이를 위해 다음 단계로 쿠키와 헤더에 CSRF 토큰을 설정하고, 서버 측에서 이를 검증하는 로직을 추가해야 합니다.
다음은 쿠키를 사용하여 CSRF 토큰을 설정하고 검증하는 방법을 설명합니다.
1. CSRF 토큰 생성 및 설정 (JSP에서 쿠키 설정)
write.do 페이지에 진입할 때 CSRF 토큰을 생성하고 쿠키로 설정합니다. 이 코드는 JavaScript를 사용하여 AJAX 요청 시 CSRF 토큰을 헤더로 전달하고, 쿠키로도 설정하는 방법을 설명합니다.
JSP 파일 수정
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<jsp:directive.include file="/WEB-INF/jsp/cms/layout/include/cmmn_taglib.jsp"/> <!-- COMMON TAG LIBS -->
<head>
<script type="text/javascript">
// 쿠키에 CSRF 토큰 설정 함수
function setCsrfTokenCookie(csrfToken) {
document.cookie = "CSRF_TOKEN=" + encodeURIComponent(csrfToken) + "; path=/; secure; HttpOnly; SameSite=Strict";
}
// CSRF 토큰 생성 함수 (서버에서 토큰 발급받아야 함)
function generateCsrfToken() {
// 서버에서 CSRF 토큰을 생성해서 전달받는 로직 필요
// 여기서는 예시로 랜덤 문자열 생성 (실제로는 서버가 관리)
return 'csrfToken-' + Math.random().toString(36).substr(2);
}
// 페이지 로드 시 CSRF 토큰을 쿠키에 설정
window.onload = function() {
var csrfToken = generateCsrfToken();
setCsrfTokenCookie(csrfToken);
}
// AJAX 요청 시 CSRF 토큰 헤더에 추가
jQuery.ajaxSetup({
beforeSend: function(xhr, settings) {
var csrfToken = document.cookie.split('; ').find(row => row.startsWith('CSRF_TOKEN=')).split('=')[1];
xhr.setRequestHeader('_csrf', decodeURIComponent(csrfToken));
}
});
// 게시글 작성 시 폼 제출
function fn_write() {
if (fn_validation_check($('#cmmnForm'))) {
var msg = "${vo.act eq 'regist' ? '등록' : '수정'}";
if (confirm(msg + ' 하시겠습니까?')) {
$('#cmmnForm').attr('action', '${vo.selfPath}edit.act');
$('#cmmnForm').submit();
}
}
}
// 취소 버튼 동작
function fn_cancel() {
$('#tabPos').val('');
$('#act').val('');
$('#cmmnForm').attr('action', '${vo.selfPath}index.do');
$('#cmmnForm').submit();
}
</script>
</head>
<body>
<form:form commandName="commonVO" name="cmmnForm" id="cmmnForm" method="post">
<form:hidden path="act" />
<form:hidden path="tabPos" />
<!-- ... 폼 입력 부분 ... -->
<div class="cont_btn_list_area">
<ul class="list tr">
<li><a href="javascript:void(0);" onclick="fn_write();" class="btn blue">등록</a></li>
<li><a href="javascript:void(0);" onclick="fn_cancel();" class="btn">취소</a></li>
</ul>
</div>
</form:form>
</body>
위 코드에서는 페이지가 로드될 때 CSRF 토큰을 생성하여 쿠키에 설정하고, 이후 AJAX 요청 시 해당 토큰을 헤더로 함께 전송하도록 구성합니다.
2. 서버 측에서 CSRF 토큰 검증 (Java Controller 수정)
서버에서는 CSRF 토큰을 검증하는 로직이 필요합니다. 클라이언트에서 전송된 헤더와 쿠키의 CSRF 토큰이 일치하는지 확인한 후, 일치하지 않으면 게시글 등록을 차단합니다.
Controller에 CSRF 검증 추가
package web.home.kor.user.notice.safety.ctr;
import java.net.URLDecoder;
import java.util.Arrays;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import cms.aspect.AccessNeedSession;
import cms.cmmn.CommonVO;
@Controller
@RequestMapping(value = "/notice/safety/")
public class HomeKorUserNoticeSafetyCtr {
// 게시글 등록/수정 요청 처리
@AccessNeedSession
@RequestMapping(value = "edit.act")
public String editAct(
@ModelAttribute("commonVO") CommonVO vo,
HttpServletRequest request,
HttpServletResponse response,
ModelMap model) throws Exception {
// CSRF 토큰 검증
String paramToken = request.getHeader("_csrf"); // 헤더로 전달된 CSRF 토큰
String cookieToken = null;
// 쿠키에서 CSRF 토큰 확인
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("CSRF_TOKEN".equals(cookie.getName())) {
cookieToken = URLDecoder.decode(cookie.getValue(), "UTF-8");
// CSRF 토큰 검증 후 쿠키 삭제 (재사용 방지)
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
break;
}
}
}
// 토큰 일치 여부 검증
if (cookieToken == null || !cookieToken.equals(paramToken)) {
throw new SecurityException("CSRF 토큰 불일치로 요청을 차단합니다.");
}
// 게시글 등록/수정 처리 로직
switch (vo.getAct()) {
case "regist":
homeKorUserNoticeSafetySvc.insertHomeKorUserNoticeSafetyDataInfo(vo);
model.addAttribute("status", "0");
break;
case "delete":
homeKorUserNoticeSafetySvc.deleteHomeKorUserNoticeSafetyData(vo);
model.addAttribute("status", "2");
break;
}
model.addAttribute("params", searchDataToJson(vo));
model.addAttribute("returnURL", vo.getSelfPath() + "success.do");
return "redirect:/notice/safety/success.do";
}
}
3. 쿠키 및 토큰 검증 흐름
- JSP 페이지에서 CSRF 토큰 설정
- window.onload 이벤트에서 CSRF 토큰을 생성하여 쿠키에 저장.
- 모든 AJAX 요청에서 해당 쿠키에 저장된 CSRF 토큰을 헤더로 추가하여 서버에 전송.
- 서버에서 CSRF 토큰 검증
- 클라이언트에서 헤더와 쿠키로 전달된 CSRF 토큰 값을 비교.
- 토큰이 일치하면 게시글 등록/수정 처리를 계속 진행.
- 토큰이 일치하지 않으면 보안 예외를 발생시켜 요청을 차단.
4. 결론
이 코드에서 쿠키를 사용한 CSRF 방어를 구현했습니다. 클라이언트는 게시글 작성 시 생성된 CSRF 토큰을 쿠키에 저장하고, 서버는 해당 토큰을 헤더와 쿠키로 검증하여 자동화 공격을 방지합니다.
** 최후의 방법
해당 코드를 헤치지 않고, 게시물의 중복 등록을 방지하기 위해 쿠키를 사용하여 중복 등록을 막는 방법을 추가할 수 있습니다. 이 방법은 게시물이 등록되면 쿠키에 일정 시간 동안 등록된 정보를 저장하고, 그 쿠키가 존재하는 동안에는 동일한 게시물 등록을 막는 방식입니다.
1. 쿠키를 사용한 중복 등록 방지 로직 추가
JavaScript 코드 (프론트엔드) 수정
게시물 등록 후, 쿠키에 등록된 게시물의 정보를 저장하고 일정 시간 동안 재등록을 방지하는 기능을 추가합니다.
// SHA-256 해시 함수 (간단한 해시 알고리즘 사용)
async function generateHash(text) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
}
// 게시물 등록 후 쿠키에 게시물 정보를 저장
function setPostCookie(postHash) {
var expireTime = new Date();
expireTime.setMinutes(expireTime.getMinutes() + 10); // 쿠키 유효기간을 10분으로 설정
document.cookie = "lastPost=" + encodeURIComponent(postHash) + "; expires=" + expireTime.toUTCString() + "; path=/";
// 등록 시간도 쿠키에 저장
var currentTime = new Date().getTime();
document.cookie = "lastPostTime=" + currentTime + "; expires=" + expireTime.toUTCString() + "; path=/";
}
// 쿠키에서 게시물 데이터와 등록 시간 가져오기
function getPostCookie() {
var cookies = document.cookie.split('; ');
var postData = null;
var postTime = null;
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].split('=');
if (cookie[0] === 'lastPost') {
postData = decodeURIComponent(cookie[1]);
} else if (cookie[0] === 'lastPostTime') {
postTime = parseInt(cookie[1]);
}
}
return { postData, postTime };
}
async function fn_write(){
/* $.each($('textarea[editor=Y]'), function(index, item) {
oEditors.getById[$(item).attr('id')].exec("UPDATE_CONTENTS_FIELD", []);
}); */
if(fn_validation_check($('#cmmnForm'))) {
var msg = "${vo.act eq 'regist' ?'등록' :'수정'}";
// 제목과 내용을 합친 데이터를 해싱 (제목 + 내용)
var postData = $('#paramKey1').val() + $('#paramKey2').val();
var postHash = await generateHash(postData); // 게시물 데이터를 해시 처리
// 쿠키에서 마지막 등록된 게시물 해시와 등록 시간 확인
var lastPostData = getPostCookie();
var lastPostHash = lastPostData.postData;
var lastPostTime = lastPostData.postTime;
var currentTime = new Date().getTime();
// 10분 내에 동일한 해시의 게시물이 등록되었는지 확인
if (lastPostHash && lastPostHash === postHash) {
alert('동일한 게시물을 반복적으로 등록할 수 없습니다.');
return; // 중복 등록 방지
}
// 짧은 시간(예: 1분) 내에 게시물이 등록된 경우
if (lastPostTime && (currentTime - lastPostTime) < 60000) { // 60000ms = 1분
alert('너무 짧은 시간 내에 게시물을 등록할 수 없습니다. 잠시 후 다시 시도해 주세요.');
return; // 빠른 반복 등록 방지
}
if(confirm(msg+' 하시겠습니까?')) {
$('#cmmnForm').attr('action', '${vo.selfPath}edit.act');
$('#cmmnForm').submit();
// 게시물 등록 후 쿠키에 저장
setPostCookie(postHash);
}
}
}
2. 설명
- 쿠키 저장 (setPostCookie): 게시물 데이터를 쿠키에 저장하여 동일한 내용이 중복 등록되지 않도록 10분 동안 유지되게 합니다.
- 쿠키 가져오기 (getPostCookie): 쿠키에서 마지막으로 등록한 게시물의 데이터를 가져와서 중복을 방지할 때 사용합니다.
- 중복 등록 확인: 게시물 제목과 내용을 합친 데이터를 postData로 저장하고, 쿠키에 저장된 값과 비교하여 동일한 게시물이 다시 등록되지 않도록 차단합니다.
- SHA-256 해시 사용: 게시물의 제목과 내용을 합친 후 해시 함수를 사용하여 변환한 데이터를 쿠키에 저장합니다. 이 해시는 미세한 데이터 변경도 검출할 수 있습니다.
- 시간 기반 중복 등록 방지: 게시물 등록 시 쿠키에 등록 시간을 저장하고, 짧은 시간(예: 1분) 내에 다시 등록하는 것을 방지합니다. 이 시간을 필요에 따라 조정할 수 있습니다.
- 쿠키에 해시와 등록 시간 저장: setPostCookie 함수는 등록된 게시물의 해시와 등록 시간을 쿠키에 저장합니다.
- 중복 검사: 게시물 해시와 등록 시간을 확인하여 동일한 게시물 또는 짧은 시간 내의 반복 등록을 차단합니다.
3. 백엔드 코드 수정 불필요
해당 방식은 클라이언트 측(프론트엔드)에서만 중복 등록을 방지하는 방법이므로, 백엔드 코드를 수정하지 않고도 동작합니다.
하지만 백엔드에서도 더욱 강력하게 중복을 방지하고자 한다면, 백엔드에서도 게시물의 등록 시간을 확인하여 일정 시간 동안 동일한 사용자가 동일한 게시물을 여러 번 등록하지 못하게 막는 방식을 추가할 수 있습니다.
관리번호 | MW_LNJ_2024_211 (1차 중) | ||
취약점명 | 약한 문자열 강도 | ||
점검일자 | 2024.08.19. ~ 2024.08.23. | ||
위험도 | 하 | 조치 시급성 | 단·중기 |
취약위치 | |||
경로 | 홈 > 마이페이지 > 정보수정 | ||
URL | home/kor/mypage/info/edit.act | ||
파라미터 | paramKey3, pw2 | ||
취약점 설명 | |||
회원 정보 수정 시 비밀번호 작성 규칙을 무시하고 약한 문자열로 비밀번호 변경 가능 | |||
상세설명 |
package web.home.kor.user.mypage.info.ctr;
import java.util.regex.Pattern;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.json.simple.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import cms.aspect.AccessNeedSession;
import cms.cmmn.CommonVO;
import cms.constant.Const;
import cms.constant.FileConst;
import cms.mapping.CMSMappingHandler;
import egovframework.rte.psl.dataaccess.util.EgovMap;
import web.home.kor.user.mypage.info.svc.HomeKorUserMypageInfoSvc;
@Controller
@RequestMapping(value="/mypage/info/")
public class HomeKorUserMypageInfoCtr extends CMSMappingHandler {
@Resource(name="homeKorUserMypageInfoSvc")
private HomeKorUserMypageInfoSvc homeKorUserMypageInfoSvc;
@AccessNeedSession
@RequestMapping(value="index.do")
public void index(
@ModelAttribute("commonVO") CommonVO vo,
HttpServletRequest request,
ModelMap model
) throws Exception {
EgovMap resultData = homeKorUserMypageInfoSvc.selectHomeKorUserMypageInfoDataInfo(vo);
model.addAttribute("fileList1", selectFileData(FileConst.SERVICE_REGISTER_ATTACH1, String.valueOf(usrSessionAt(request).get("no")) ));
model.addAttribute("fileList2", selectFileData(FileConst.SERVICE_REGISTER_ATTACH2, String.valueOf(usrSessionAt(request).get("no")) ));
model.addAttribute("fileList3", selectFileData(FileConst.SERVICE_REGISTER_ATTACH3, String.valueOf(usrSessionAt(request).get("no")) ) );
model.addAttribute("gubunList", commonSvc.selectCategoryParentDataList("CWFTc5ocrT"));
model.addAttribute("resultData", resultData);
}
// 비밀번호 복잡도 검사 메서드
private boolean isPasswordStrong(String password) {
if (password.length() < 6 || password.length() > 12) {
return false;
}
boolean hasUpperCase = Pattern.compile("[A-Z]").matcher(password).find();
boolean hasLowerCase = Pattern.compile("[a-z]").matcher(password).find();
boolean hasDigit = Pattern.compile("[0-9]").matcher(password).find();
boolean hasSpecialChar = Pattern.compile("[!@#$%^&*(),.?\":{}|<>]").matcher(password).find();
// 4종류 중 2종류 이상 포함 여부 확인
int count = 0;
if (hasUpperCase) count++;
if (hasLowerCase) count++;
if (hasDigit) count++;
if (hasSpecialChar) count++;
return count >= 2;
}
@AccessNeedSession
@RequestMapping(value="edit.act")
public void editAct(
@ModelAttribute("commonVO") CommonVO vo,
HttpServletRequest request,
HttpSession session,
ModelMap model
) throws Exception {
// 비밀번호가 변경되었을 경우, 비밀번호 복잡도 검사를 수행
if (stringUtil.isNotEmpty(vo.getParamKey3())) {
String password = vo.getParamKey3();
if (!isPasswordStrong(password)) {
model.addAttribute("status", "0");
model.addAttribute("errorMsg", "비밀번호는 6자 이상 12자 미만이어야 하며, 영문 대/소문자, 숫자, 특수문자 중 2종류 이상을 포함해야 합니다.");
return;
}
vo.setParamKey3(encryptModule.encryptSHA256(password)); // 비밀번호를 암호화 후 저장
}
if (stringUtil.isNotEmpty(vo.getParamKey2())) vo.setParamKey2(encryptModule.encryptAREA(vo.getParamKey2()));
if (stringUtil.isNotEmpty(vo.getParamKey1())) vo.setParamKey1(encryptModule.encryptAREA(vo.getParamKey1()));
if (stringUtil.isNotEmpty(vo.getParamKey11())) vo.setParamKey11(encryptModule.encryptAREA(vo.getParamKey11()));
homeKorUserMypageInfoSvc.updateHomeKorUserMypageInfoData(vo);
homeKorUserMypageInfoSvc.updateAccountPw(vo);
vo.setIdx(String.valueOf(usrSessionAt(request).get("no")));
removeFileData(vo);
insertFileData(FileConst.SERVICE_REGISTER_ATTACH1, vo.getIdx(), vo.getFileDataList1());
insertFileData(FileConst.SERVICE_REGISTER_ATTACH2, vo.getIdx(), vo.getFileDataList2());
insertFileData(FileConst.SERVICE_REGISTER_ATTACH3, vo.getIdx(), vo.getFileDataList3());
session.removeAttribute(Const.USER_SESSION_SET);
EgovMap map = homeKorUserMypageInfoSvc.selectHomeKorUserMypageInfoAct(vo);
map.put("memberType", "A");
request.getSession().setAttribute(Const.USER_SESSION_SET, map);
model.addAttribute("status", "1");
model.addAttribute("params", searchDataToJson(vo));
model.addAttribute("returnURL", vo.getSelfPath() + "index.do");
}
@AccessNeedSession
@RequestMapping(value="withdraw.ajax", produces = "application/json; charset=utf8")
public @ResponseBody JSONObject withdraw(
@ModelAttribute("commonVO") CommonVO vo,
HttpServletRequest request,
ModelMap model
) throws Exception {
JSONObject json = new JSONObject();
if (stringUtil.isEmpty(String.valueOf(usrSessionAt(request).get("no")))) {
json.put("result", "400");
json.put("msg", "키 값이 누락되었습니다.");
return json;
}
try {
homeKorUserMypageInfoSvc.updateWithdrawMember(vo);
homeKorUserMypageInfoSvc.updateWithdrawMemberInfo(vo);
json.put("result", "200");
} catch (Exception e) {
e.printStackTrace();
json.put("result", "500");
}
return json;
}
}
관리번호 | MW_LNJ_2024_207 (1차 완료) -다시 확인 | ||
취약점명 | 불충분한 인증 및 인가 | ||
점검일자 | 2024.08.19. ~ 2024.08.23. | ||
위험도 | 상 | 조치 시급성 | 즉시 |
취약위치 | |||
경로 | 홈 > 알림마당 > 나의 제안(신고) 결과 확인하기 | ||
URL | /home/kor/notice/safety/detail.do | ||
파라미터 | idx | ||
취약점 설명 | |||
권한 프로세스 검증 미흡으로 타인의 비밀글 열람 가능 | |||
상세설명 | |||
1) 안전제안 임의 게시글 작성 후 게시글 열람 시도 2) 열람 시 전달되는 게시글 번호 변조 시도(위: 변조 전, 아래: 변조 후) 3) 변조 시 타인 게시물 열람 가능 확인 |
public void detail(
@ModelAttribute("commonVO") CommonVO vo,
HttpServletRequest request,
ModelMap model
) throws Exception {
// 현재 세션의 사용자 ID를 가져옴
String currentUserId = String.valueOf(usrSessionAt(request).get("id"));
System.out.println("currentUserId : " + currentUserId);
if(stringUtil.isNotEmpty(vo.getIdx())) {
EgovMap resultData = homeKorUserNoticeSafetySvc.selectHomeKorUserNoticeSafetyDataInfo(vo);
// 게시글 작성자의 ID 가져옴
String postOwnerId = String.valueOf(resultData.get("id"));
// 게시글 작성자가 아닌 경우 접근 차단
if (!currentUserId.equals(postOwnerId)) {
// 접근 권한 없음: 에러 페이지 또는 게시물 목록으로 리다이렉트
throw new IllegalStateException("권한이 없는 게시물입니다.");
}
System.out.println("postOwnerId : " + postOwnerId);
model.addAttribute("fileList1", selectFileData(FileConst.NOTICE_SAFETY_ATTACH,vo.getIdx()));
model.addAttribute("fileList2", selectFileData(FileConst.NOTICE_SAFETY_ATTACH2,vo.getIdx()));
model.addAttribute("resultData", resultData);
}
model.addAttribute("layout_option", "sub");
}
관리번호 | MW_LNJ_2024_208 (1차 완료) | ||
취약점명 | 불충분한 인증 및 인가 | ||
점검일자 | 2024.08.19. ~ 2024.08.23. | ||
위험도 | 상 | 조치 시급성 | 즉시 |
취약위치 | |||
경로 | 홈 > 알림마당 > 공지사항 | ||
URL | home/kor/board.do | ||
파라미터 | act | ||
취약점 설명 | |||
게시글 작성 권한이 없는 사용자가 공지사항 등록/삭제 가능 | |||
상세설명 | |||
1) 공지사항 게시판 내 작성 권한이 존재하지 않으나, 소스코드 내 스크립트 실행 2) 스크립트 실행 시 작성 페이지 진입 확인 및 게시글 작성 3) 등록 권한이 존재하지 않으나, 소스코드 내 스크립트 실행 4) 공지사항 정상 등록 확인 5) 삭제 권한이 존재하지 않으나, 소스코드 내 스크립트 실행 6) 공지사항 정상 삭제 확인 가능 |