From a7fe811fb9e40b219863160f444eba4d361cb3f6 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Tue, 30 Dec 2025 21:37:37 +0900 Subject: [PATCH] Refactor Summernote initialization for consistency and improved readability --- WebContent/WEB-INF/web.xml | 45 +--- WebContent/jsp/common/include/script.jsp | 239 ++++++++++++++++++ .../onl/apim/portalfaq/portalFaqManDetail.jsp | 24 +- .../portalinquiry/portalInquiryManDetail.jsp | 24 +- .../portalnotice/portalNoticeManDetail.jsp | 24 +- .../apim/portalterms/portalTermsManDetail.jsp | 26 +- .../apim/template/messageTemplateManPopup.jsp | 14 +- .../onl/transaction/apim/apiSpecManPopup.jsp | 18 +- .../eai/rms/common/filter/RequestWrapper.java | 50 +++- 9 files changed, 320 insertions(+), 144 deletions(-) diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index dc7a0ea..a73128b 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -9,16 +9,7 @@ contextConfigLocation /WEB-INF/applicationContext.xml - - - CrossScriptingFilter - com.eactive.eai.rms.common.filter.CrossScriptingFilter - - - CrossScriptingFilter - /* - - + encodingFilter org.springframework.web.filter.CharacterEncodingFilter @@ -31,30 +22,20 @@ true + + + encodingFilter + /* + + + - encodingFilterEUCKR - org.springframework.web.filter.CharacterEncodingFilter - - encoding - utf-8 - - - forceEncoding - true - + CrossScriptingFilter + com.eactive.eai.rms.common.filter.CrossScriptingFilter - encodingFilterEUCKR - *.excel - - - encodingFilter - *.file - - - - encodingFilter - *.json + CrossScriptingFilter + /* + + ", Pattern.CASE_INSENSITIVE), - Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", - Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), - Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", - Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("", Pattern.CASE_INSENSITIVE), Pattern.compile("", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), @@ -46,6 +43,16 @@ class RequestWrapper extends HttpServletRequestWrapper { Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL) }; + // src 속성 화이트리스트 패턴 - data:image/만 허용 + private static final Pattern SRC_ATTR_PATTERN = Pattern.compile( + "(src\\s*=\\s*)([\"'])([^\"']*?)\\2", + Pattern.CASE_INSENSITIVE); + + // 허용되는 src 값 패턴 (화이트리스트) - data:image/만 허용 + private static final Pattern ALLOWED_SRC_PATTERN = Pattern.compile( + "^data:image/.*", + Pattern.CASE_INSENSITIVE); + public RequestWrapper(HttpServletRequest servletRequest) { super(servletRequest); @@ -123,6 +130,10 @@ class RequestWrapper extends HttpServletRequestWrapper { // script 문자열을 완전히 제거 value = value.replaceAll("(?i)\\bscript\\b", ""); + + // src 속성 화이트리스트 필터링 - 허용된 프로토콜만 통과 + value = filterSrcAttribute(value); + StringBuilder sb = new StringBuilder(); value = convertChars(value, sb); @@ -133,6 +144,37 @@ class RequestWrapper extends HttpServletRequestWrapper { return value; } + /** + * src 속성을 화이트리스트 방식으로 필터링 + * 허용: data:image/ (Base64 이미지) + * 비허용: 그 외 모든 프로토콜 + */ + private String filterSrcAttribute(String value) { + if (value == null || !value.toLowerCase().contains("src")) { + return value; + } + + Matcher matcher = SRC_ATTR_PATTERN.matcher(value); + StringBuffer result = new StringBuffer(); + + while (matcher.find()) { + String srcPrefix = matcher.group(1); // src= + String quote = matcher.group(2); // " 또는 ' + String srcValue = matcher.group(3); // src 값 + + // 빈 값이거나 화이트리스트에 매칭되면 유지 + if (srcValue.isEmpty() || ALLOWED_SRC_PATTERN.matcher(srcValue).matches()) { + matcher.appendReplacement(result, Matcher.quoteReplacement(srcPrefix + quote + srcValue + quote)); + } else { + // 허용되지 않는 프로토콜은 src="" 로 대체 + matcher.appendReplacement(result, Matcher.quoteReplacement(srcPrefix + quote + quote)); + } + } + matcher.appendTail(result); + + return result.toString(); + } + // '(', ')', '"' 문자는 getMethod()가 "get"일 때만 변환됨. // 이는 gridData에서 사용되는 큰따옴표(")와 변환 함수에서 사용하는 괄호('(', ')')의 처리를 위함. private String convertChars(String value, StringBuilder sb) {