quodana 소스 품질관리 및 보안 취약점 점검 대응

multiDB
박성영 7 months ago
parent 2744f4cd27
commit 8aac5e0bfa

@ -2,6 +2,7 @@ package egovframework.config;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@ -48,7 +49,7 @@ public class EgovConfigWeb implements WebMvcConfigurer, ApplicationContextAware
* @see egovframework.exception.EgovExceptionAdvice
*/
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
public void configureHandlerExceptionResolvers(@Nullable List< HandlerExceptionResolver> resolvers) {
// Exception handling is now done using @ControllerAdvice
}

@ -1,5 +1,6 @@
package egovframework.filter;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
@ -21,7 +22,7 @@ public class XssFilter extends OncePerRequestFilter {
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
protected void doFilterInternal(@Nullable HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// XSS 방지를 위한 헤더 설정

@ -46,8 +46,8 @@ public class XssUtil {
.replaceAll("&quot;", "\"")
.replaceAll("&#x27;", "'")
.replaceAll("&#x2F;", "/")
.replaceAll("&#40;", "\\(")
.replaceAll("&#41;", "\\)");
.replaceAll("&#40;", "(")
.replaceAll("&#41;", ")");
}
/**

@ -14,6 +14,7 @@ import go.kr.project.login.service.LoginService;
import go.kr.project.system.menu.model.MenuVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -72,7 +73,7 @@ public class AuthInterceptor implements HandlerInterceptor {
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler) throws Exception {
String requestURI = request.getRequestURI();
// 접근 제어 예외 URL 패턴은 WebMvcConfigurer에서 처리됨

@ -117,7 +117,7 @@ public class ClientInfoUtil {
return "iOS";
} else if (userAgent.contains("iphone os") || userAgent.contains("cpu os")) {
// iOS 버전 추출
int startIndex = userAgent.indexOf("iphone os ") != -1 ? userAgent.indexOf("iphone os ") + 10 : userAgent.indexOf("cpu os ") + 7;
int startIndex = userAgent.contains("iphone os ") ? userAgent.indexOf("iphone os ") + 10 : userAgent.indexOf("cpu os ") + 7;
if (startIndex != -1) {
int endIndex = userAgent.indexOf(" ", startIndex);
if (endIndex != -1) {
@ -234,19 +234,19 @@ public class ClientInfoUtil {
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}

@ -101,7 +101,7 @@ public class EgovFileScrty {
if (password == null) return "";
if (id == null) return ""; // KISA 보안약점 조치 (2018-12-11, 신용호)
byte[] hashValue = null; // 해쉬값
byte[] hashValue; // 해쉬값
MessageDigest md = MessageDigest.getInstance("SHA-256");
@ -126,7 +126,7 @@ public class EgovFileScrty {
return "";
}
byte[] hashValue = null; // 해쉬값
byte[] hashValue; // 해쉬값
MessageDigest md = MessageDigest.getInstance("SHA-256");
@ -147,7 +147,7 @@ public class EgovFileScrty {
* @throws Exception
*/
public static boolean checkPassword(String data, String encoded, byte[] salt) throws Exception {
byte[] hashValue = null; // 해쉬값
byte[] hashValue; // 해쉬값
MessageDigest md = MessageDigest.getInstance("SHA-256");
@ -158,19 +158,4 @@ public class EgovFileScrty {
return MessageDigest.isEqual(hashValue, Base64.decodeBase64(encoded.getBytes()));
}
/*
public static void main(String[] args) {
try {
String password = "abc";
String salt = "def";
String first = encryptPassword(password, salt.getBytes());
String second = encryptPassword(password, salt.getBytes());
System.out.println(password + " => " + first + " : " + checkPassword(password, first, salt.getBytes()));
System.out.println(password + " => " + second + " : " + checkPassword(password, second, salt.getBytes()));
} catch (Exception ex) {
ex.printStackTrace();
}
}
*/
}

@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
@ -12,12 +13,15 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* /
@ -49,6 +53,86 @@ public class FileUtil {
@Value("${file.upload.real-file-delete:true}")
private boolean realFileDelete;
/** 허용된 파일 확장자 목록 */
private List<String> allowedExtensionList;
/**
*
* allowedExtensions allowedExtensionList
*/
@PostConstruct
public void init() {
allowedExtensionList = Arrays.stream(allowedExtensions.split(","))
.map(String::trim)
.map(String::toLowerCase)
.collect(Collectors.toList());
}
/**
*
* (Path Traversal)
* @param path
* @param isFileName (true: , false: )
* @throws IOException
*/
private void validatePath(String path, boolean isFileName) throws IOException {
if (path == null || path.contains("..") || path.contains("/") || path.contains("\\")) {
String type = isFileName ? "파일명" : "디렉토리 경로";
throw new IOException("잘못된 " + type + "입니다: " + path);
}
}
/**
*
* @param fileExt
* @return
*/
private boolean isAllowedExtension(String fileExt) {
return allowedExtensionList.contains(fileExt.toLowerCase());
}
/**
*
* @param fileName
* @param userAgent User-Agent
* @return
* @throws IOException
*/
private String getEncodedFilename(String fileName, String userAgent) throws IOException {
if (userAgent.contains("MSIE") || userAgent.contains("Trident") || userAgent.contains("Chrome")) {
// IE, Chrome
return URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
} else if (userAgent.contains("Firefox")) {
// Firefox
return "\"" + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + "\"";
} else {
// 기타 브라우저
return URLEncoder.encode(fileName, "UTF-8");
}
}
/**
* FileVO
* @param originalFilename
* @param storedFilename
* @param subDir
* @param fileSize
* @param fileExt
* @param contentType
* @return FileVO
*/
private FileVO createFileVO(String originalFilename, String storedFilename, String subDir,
long fileSize, String fileExt, String contentType) {
FileVO fileVO = new FileVO();
fileVO.setOriginalFileNm(originalFilename);
fileVO.setStoredFileNm(storedFilename);
fileVO.setFilePath(subDir);
fileVO.setFileSize(fileSize);
fileVO.setFileExt(fileExt);
fileVO.setContentType(contentType);
return fileVO;
}
/**
*
* @param files
@ -57,78 +141,136 @@ public class FileUtil {
* @throws IOException
*/
public List<FileVO> uploadFiles(List<MultipartFile> files, String subDir) throws IOException {
List<FileVO> uploadedFiles = new ArrayList<>();
long totalSize = 0;
// 파일 유효성 검증
validateFiles(files);
// 디렉토리 경로 검증
validatePath(subDir, false);
// 디렉토리 생성
String uploadDir = uploadPath + File.separator + subDir;
createDirectoryIfNotExists(uploadDir);
// 파일 업로드 처리
return processFileUploads(files, subDir, uploadDir);
}
/**
*
* @param files
* @throws IOException
*/
private void validateFiles(List<MultipartFile> files) throws IOException {
// 파일 개수 검증
int validFileCount = 0;
for (MultipartFile file : files) {
if (!file.isEmpty()) {
validFileCount++;
}
}
int validFileCount = (int) files.stream()
.filter(file -> !file.isEmpty())
.count();
if (validFileCount > maxFiles) {
throw new IOException("파일 개수가 제한을 초과했습니다. 최대 " + maxFiles + "개까지 가능합니다.");
}
}
// 디렉토리 생성
String uploadDir = uploadPath + File.separator + subDir;
createDirectoryIfNotExists(uploadDir);
// 파일 확장자 목록
String[] extensions = allowedExtensions.split(",");
/**
*
* @param files
* @param subDir
* @param uploadDir
* @return
* @throws IOException
*/
private List<FileVO> processFileUploads(List<MultipartFile> files, String subDir, String uploadDir) throws IOException {
List<FileVO> uploadedFiles = new ArrayList<>();
long totalSize = 0;
for (MultipartFile file : files) {
if (file.isEmpty()) continue;
// 파일 크기 검증
if (file.getSize() > maxFileSize * 1024 * 1024) {
throw new IOException("파일 크기가 제한을 초과했습니다. 최대 " + maxFileSize + "MB까지 가능합니다.");
}
validateFileSize(file, totalSize);
totalSize += file.getSize();
if (totalSize > maxTotalSize * 1024 * 1024) {
throw new IOException("총 파일 크기가 제한을 초과했습니다. 최대 " + maxTotalSize + "MB까지 가능합니다.");
}
// 원본 파일명 및 확장자 추출
String originalFilename = file.getOriginalFilename();
String fileExt = getFileExtension(originalFilename);
// 확장자 검증
boolean isAllowedExtension = false;
for (String ext : extensions) {
if (ext.trim().equalsIgnoreCase(fileExt)) {
isAllowedExtension = true;
break;
}
}
// 파일 저장 및 정보 생성
FileVO fileVO = saveFile(file, subDir, uploadDir);
uploadedFiles.add(fileVO);
}
if (!isAllowedExtension) {
throw new IOException("허용되지 않은 파일 형식입니다: " + fileExt);
}
return uploadedFiles;
}
// UUID를 이용한 저장 파일명 생성
String storedFilename = UUID.randomUUID().toString() + "." + fileExt;
/**
*
* @param file
* @param currentTotalSize
* @throws IOException
*/
private void validateFileSize(MultipartFile file, long currentTotalSize) throws IOException {
// 단일 파일 크기 검증
if (file.getSize() > maxFileSize * 1024 * 1024) {
throw new IOException("파일 크기가 제한을 초과했습니다. 최대 " + maxFileSize + "MB까지 가능합니다.");
}
// 파일 저장
Path filePath = Paths.get(uploadDir, storedFilename);
Files.write(filePath, file.getBytes());
// 총 파일 크기 검증
if (currentTotalSize + file.getSize() > maxTotalSize * 1024 * 1024) {
throw new IOException("총 파일 크기가 제한을 초과했습니다. 최대 " + maxTotalSize + "MB까지 가능합니다.");
}
}
// 파일 정보 생성
FileVO fileVO = new FileVO();
fileVO.setOriginalFileNm(originalFilename);
fileVO.setStoredFileNm(storedFilename);
fileVO.setFilePath(subDir);
fileVO.setFileSize(file.getSize());
fileVO.setFileExt(fileExt);
fileVO.setContentType(file.getContentType());
/**
*
* @param file
* @param subDir
* @param uploadDir
* @return
* @throws IOException
*/
private FileVO saveFile(MultipartFile file, String subDir, String uploadDir) throws IOException {
// 원본 파일명 및 확장자 추출
String originalFilename = file.getOriginalFilename();
String fileExt = getFileExtension(originalFilename);
uploadedFiles.add(fileVO);
// 확장자 검증
if (!isAllowedExtension(fileExt)) {
throw new IOException("허용되지 않은 파일 형식입니다: " + fileExt);
}
return uploadedFiles;
// UUID를 이용한 저장 파일명 생성
String storedFilename = UUID.randomUUID() + "." + fileExt;
// 파일명 검증
validatePath(storedFilename, true);
// 파일 저장 경로 생성 및 검증
Path filePath = createAndValidateFilePath(uploadDir, storedFilename);
// 파일 저장
Files.write(filePath, file.getBytes());
// 파일 정보 생성 및 반환
return createFileVO(originalFilename, storedFilename, subDir,
file.getSize(), fileExt, file.getContentType());
}
/**
*
* @param uploadDir
* @param storedFilename
* @return
* @throws IOException
*/
private Path createAndValidateFilePath(String uploadDir, String storedFilename) throws IOException {
Path filePath = Paths.get(uploadDir).normalize().resolve(storedFilename).normalize();
// 생성된 경로가 업로드 디렉토리 내에 있는지 확인
File targetFile = filePath.toFile();
String canonicalUploadDir = new File(uploadDir).getCanonicalPath();
String canonicalTargetPath = targetFile.getCanonicalPath();
if (!canonicalTargetPath.startsWith(canonicalUploadDir)) {
throw new IOException("잘못된 파일 경로입니다.");
}
return filePath;
}
/**
@ -139,6 +281,30 @@ public class FileUtil {
* @throws IOException
*/
public void downloadFile(FileVO fileVO, HttpServletRequest request, HttpServletResponse response) throws IOException {
// 파일 정보 검증 및 파일 객체 생성
File file = validateAndGetFile(fileVO);
// 파일 확장자 검증
validateFileExtension(fileVO.getOriginalFileNm());
// 브라우저별 인코딩된 파일명 생성
String userAgent = request.getHeader("User-Agent");
String encodedFilename = getEncodedFilename(fileVO.getOriginalFileNm(), userAgent);
// 응답 헤더 설정
setResponseHeaders(response, fileVO, file, encodedFilename);
// 파일 전송
sendFile(file, response);
}
/**
*
* @param fileVO
* @return
* @throws IOException
*/
private File validateAndGetFile(FileVO fileVO) throws IOException {
// 파일 경로 생성
String filePath = uploadPath + File.separator + fileVO.getFilePath() + File.separator + fileVO.getStoredFileNm();
File file = new File(filePath);
@ -154,53 +320,53 @@ public class FileUtil {
throw new IOException("잘못된 파일 경로입니다.");
}
// 파일 확장자 검증
String fileExt = getFileExtension(fileVO.getOriginalFileNm());
String[] extensions = allowedExtensions.split(",");
boolean isAllowedExtension = false;
for (String ext : extensions) {
if (ext.trim().equalsIgnoreCase(fileExt)) {
isAllowedExtension = true;
break;
}
}
return file;
}
if (!isAllowedExtension) {
/**
*
* @param filename
* @throws IOException
*/
private void validateFileExtension(String filename) throws IOException {
String fileExt = getFileExtension(filename);
if (!isAllowedExtension(fileExt)) {
throw new IOException("허용되지 않은 파일 형식입니다: " + fileExt);
}
}
// 브라우저 종류 확인
String userAgent = request.getHeader("User-Agent");
String encodedFilename;
// 브라우저별 인코딩 처리
if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
// IE
encodedFilename = URLEncoder.encode(fileVO.getOriginalFileNm(), "UTF-8").replaceAll("\\+", "%20");
} else if (userAgent.contains("Firefox")) {
// Firefox
encodedFilename = "\"" + new String(fileVO.getOriginalFileNm().getBytes("UTF-8"), "ISO-8859-1") + "\"";
} else if (userAgent.contains("Chrome")) {
// Chrome
encodedFilename = URLEncoder.encode(fileVO.getOriginalFileNm(), "UTF-8").replaceAll("\\+", "%20");
} else {
// 기타 브라우저
encodedFilename = URLEncoder.encode(fileVO.getOriginalFileNm(), "UTF-8");
}
// 응답 헤더 설정
/**
*
* @param response HTTP
* @param fileVO
* @param file
* @param encodedFilename
*/
private void setResponseHeaders(HttpServletResponse response, FileVO fileVO, File file, String encodedFilename) {
// 컨텐츠 타입 및 길이 설정
response.setContentType(fileVO.getContentType());
response.setContentLength((int) file.length());
// 다운로드 관련 헤더 설정
response.setHeader("Content-Disposition", "attachment; filename=" + encodedFilename);
response.setHeader("Content-Transfer-Encoding", "binary");
// 캐시 관련 헤더 설정
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
// XSS 방지를 위한 헤더 설정
// 보안 관련 헤더 설정
response.setHeader("X-Content-Type-Options", "nosniff");
}
// 파일 전송
/**
*
* @param file
* @param response HTTP
* @throws IOException
*/
private void sendFile(File file, HttpServletResponse response) throws IOException {
try (FileInputStream fis = new FileInputStream(file);
OutputStream os = response.getOutputStream()) {
@ -225,15 +391,44 @@ public class FileUtil {
return true;
}
String filePath = uploadPath + File.separator + fileVO.getFilePath() + File.separator + fileVO.getStoredFileNm();
try {
// 파일 경로 검증 및 파일 객체 생성
File file = getValidatedFileForDelete(fileVO);
// 파일 존재 여부 확인 및 삭제
return file.exists() && file.delete();
} catch (IOException e) {
// 로그 기록 등의 처리를 추가할 수 있음
return false;
}
}
/**
*
* @param fileVO
* @return
* @throws IOException
*/
private File getValidatedFileForDelete(FileVO fileVO) throws IOException {
// 파일 경로 및 파일명 검증
String subDir = fileVO.getFilePath();
String storedFilename = fileVO.getStoredFileNm();
// 경로 검증
validatePath(subDir, false);
validatePath(storedFilename, true);
// 파일 경로 생성
String filePath = uploadPath + File.separator + subDir + File.separator + storedFilename;
File file = new File(filePath);
// 파일 존재 여부 확인 및 삭제
if (file.exists()) {
return file.delete();
// 보안 검사: 경로 검증 (경로 탐색 공격 방지)
String canonicalPath = file.getCanonicalPath();
if (!canonicalPath.startsWith(new File(uploadPath).getCanonicalPath())) {
throw new IOException("잘못된 파일 경로입니다.");
}
return false;
return file;
}
/**
@ -250,12 +445,18 @@ public class FileUtil {
/**
*
* @param filename
* @return
* @return ( )
*/
private String getFileExtension(String filename) {
if (filename == null || filename.isEmpty() || !filename.contains(".")) {
if (filename == null || filename.isEmpty()) {
return "";
}
return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
int lastDotIndex = filename.lastIndexOf(".");
if (lastDotIndex == -1 || lastDotIndex == filename.length() - 1) {
return "";
}
return filename.substring(lastDotIndex + 1).toLowerCase();
}
}

@ -11,6 +11,7 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@ -63,7 +64,7 @@ public class ExcelHandler<T> {
}
private T mapRowToDto(Row row, Class<T> clazz, List<String> excelHeaderList) {
T dataDto = null;
T dataDto;
try {
dataDto = clazz.getDeclaredConstructor().newInstance();
@ -77,7 +78,7 @@ public class ExcelHandler<T> {
for (Field field : dtoFields) {
if (field.isAnnotationPresent(ExcelColumn.class)) {
ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
if (annotation.headerName().equals(excelHeaderName)) {
if (Objects.requireNonNull(annotation).headerName().equals(excelHeaderName)) {
field.setAccessible(true);
setFieldValue(field, dataDto, cell);
break;

@ -35,7 +35,7 @@ public class ExcelMetadataFactory { // (2)
}
private String getSheetName(Class<?> clazz) {
ExcelSheet annotation = (ExcelSheet)getAnnotation(clazz, ExcelSheet.class);
ExcelSheet annotation = getAnnotation(clazz, ExcelSheet.class);
if (annotation != null) {
return annotation.name();
}

@ -199,7 +199,7 @@ public class BbsNoticeServiceImpl extends EgovAbstractServiceImpl implements Bbs
}
// FileUtil을 사용하여 파일 업로드
List<FileVO> uploadedFiles = null;
List<FileVO> uploadedFiles;
try {
uploadedFiles = fileUtil.uploadFiles(validFiles, "bbs/notice");
} catch (IOException e) {

@ -18,6 +18,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
/**
* packageName : go.kr.project.common.controller
@ -65,7 +66,7 @@ public class HtmlEditorController {
// 이미지 파일 확장자 검증
String originalFilename = image.getOriginalFilename();
String fileExt = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
String fileExt = Objects.requireNonNull(originalFilename).substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
if (!fileExt.matches("jpg|jpeg|png|gif")) {
return ApiResponseUtil.error("허용되지 않은 이미지 형식입니다. (jpg, jpeg, png, gif만 가능)");

@ -241,7 +241,7 @@ public class HtmlEditorServiceImpl extends EgovAbstractServiceImpl implements Ht
}
// FileUtil을 사용하여 파일 업로드
List<FileVO> uploadedFiles = null;
List<FileVO> uploadedFiles;
try {
uploadedFiles = fileUtil.uploadFiles(validFiles, "common/html_editor");
} catch (IOException e) {

@ -22,6 +22,8 @@ import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@ -69,8 +71,15 @@ public class LoginController {
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("savedUserId".equals(cookie.getName())) {
savedUserId = cookie.getValue();
isSaveId = true;
try {
// URL 디코딩하여 원래 값으로 복원
savedUserId = java.net.URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.toString());
isSaveId = true;
} catch (Exception e) {
// 디코딩 실패 시 원래 값 사용
savedUserId = cookie.getValue();
isSaveId = true;
}
break;
}
}
@ -121,15 +130,21 @@ public class LoginController {
// 아이디 저장 처리
if ("Y".equals(saveId)) {
// 아이디 저장 쿠키 생성 (유효기간 7일)
Cookie cookie = new Cookie("savedUserId", userAcnt);
// HTTP 응답 분할 취약점 방지를 위해 URLEncoder.encode 사용
String encodedUserAcnt = URLEncoder.encode(userAcnt, StandardCharsets.UTF_8.toString());
Cookie cookie = new Cookie("savedUserId", encodedUserAcnt);
cookie.setMaxAge(60 * 60 * 24 * 7);
cookie.setPath("/");
cookie.setHttpOnly(true); // XSS 공격 방지
cookie.setSecure(request.isSecure()); // HTTPS 연결에서만 쿠키 전송
response.addCookie(cookie);
} else {
// 아이디 저장 쿠키 삭제
Cookie cookie = new Cookie("savedUserId", "");
cookie.setMaxAge(0);
cookie.setPath("/");
cookie.setHttpOnly(true); // XSS 공격 방지
cookie.setSecure(request.isSecure()); // HTTPS 연결에서만 쿠키 전송
response.addCookie(cookie);
}

@ -113,7 +113,7 @@ public class MypageServiceImpl extends EgovAbstractServiceImpl implements Mypage
@Override
public boolean checkPassword(SystemUserVO user) {
// 비밀번호 암호화
String encryptedPassword =null;
String encryptedPassword;
try {
encryptedPassword = EgovFileScrty.encryptPassword(user.getCurrentPasswd(), user.getUserId());
} catch (Exception e) {

Loading…
Cancel
Save