Initial commit

This commit is contained in:
2026-05-29 17:49:25 +09:00
commit 330105cb27
1081 changed files with 148694 additions and 0 deletions
@@ -0,0 +1,7 @@
= EU GDPR
== 개요
. GDPR이란? +
2018년 5월 25일부터 시행되는 EU(유럽연합)의 개인정보보호 법령이며, 동 법령 위반시 과징금 등 행정처분이 부과될 수 있어 EU와 거래하는 우리나라 기업도 이 법에 위반되지 않도록 주의할 필요가 있다.
. EU와 거래하는 시스템은 이용약관동의 관리에서 '유럽연합 개인정보보호 규정'이라는 약관을 추가하여 사용자의 동의를 받아야 한다.
* 세부적인 내용은 <<_약관_관리,약관 관리>> 매뉴얼을 참조한다.
@@ -0,0 +1,39 @@
= SQL Injection
== 개요
표준개발라이브러리에서는 MyBatis의 PreparedStatement 를 사용한 value injection을 원칙으로 사용하기 때문에 java 혹은 jsp에서 sql을 만들지 않는다면 근본적으로 sql injection이 발생하지 않는다.
단, ${} 매핑을 사용 할 경우 SDLComparator를 사용해야 한다.
=== SQL Injeciton 공격 예
MyBatis에서는 #{}, ${} 두 변수 형태를 제공한다. #{}의 경우엔 SQL Injection 공격이 불가하지만 ${}는 값이 직접 매핑되기 때문에 SQL Injeciton에 노출되어 있다.
${}에 매핑될 값은 사용자가 입력값(파라미터를 변조할수 있는)을 사용할 경우 보안 취약점에 노출되게 된다.
[source,xml]
----
<select id="getPerson" parameterType="string" resultType="org.application.vo.Person">
SELECT * FROM PERSON WHERE NAME = #{name} AND PHONE LIKE '${phone}';
</select>
----
위의 경우
[source,xml]
----
SELECT * FROM PERSON WHERE NAME = ? and PHONE LIKE 'A%'; DELETE FROM PERSON; --'
----
실행이 가능하다.
따라서, ${}에 매핑될 값은 조작이 불가능 하도록 사용자 입력값을 코드로 입력 할 수 있도록 하고, 서버에서 코드에 맞는 스트링을 조합해서 실행 할 수 있도록 해야한다.
=== SDLComparator 적용
SDL에서는 개발자의 시큐어 코딩 실수로 인한 SQL Injection 실행 방지를 위해 MyBatis에서 변수 매핑 전에 허용된 문자만 사용 할 수 있도록 함수를 제공하고 있다.
.사용방법
[source,xml]
----
<if test="@com.samsung.SdlComparator@isNotEmptyForDynamicSql(orderBy)">
ORDER BY ${orderBy}
</if>
----
+21
View File
@@ -0,0 +1,21 @@
= XSS 방지
== 개요
XSS(Cross Site Scripting)는 JavaScript등으로 작성된 악성 스크립트 코드를 웹 게시판 등에 삽입해 세션을 가로채거나 공격자가 의도한대로 행동하도록 만드는 공격이다.
=== XSS 방지 적용
표준개발라이브러리에서는 HTML태그를 허용하는 게시판에 대해 화이트리스트를 선정하여 해당 태그만 허용되도록 하고 있다.
.사용방법
[source,java]
----
@Operation(summary = "게시글 상세조회")
@GetMapping("/posts/{postId}")
public Post getPost(
@Parameter("게시글 ID") @PathVariable String postId) {
Post post = postService.getPost(postId);
post.setPostDetail(SdlHtmlPolicy.POLICY_DEFINITION.sanitize( post.getPostDetail())); // <1>
return post;
}
----
<1> 게시글 본문을 SdlHtmlPolicy.POLICY_DEFINITION에 정의된 정책을 기반으로 허용된 태그만 가능하도록 처리
@@ -0,0 +1,93 @@
= 개인정보 사용 이력 관리
== 개요
사용자 정보를 조회한 이력을 남긴다. 관련 법에 따라 일정기간 동안 보관한다.
=== 사용자 정보 조회 이력 관리
* UserController에서 사용자 정보 관련 메서드 호출시, UserService의 writeUserHistoryLog 메서드를 호출하고 있다.
* log4j2.xml에 설정한 파일에 이력이 남는다.
.UserController.class
[source,java]
----
@Operation(summary = "사용자 목록 조회")
@GetMapping("/auth/users")
public PagingResult<User> getUserPagingList( @ModelAttribute UserSearchDto searchDto) {
PagingResult<User> resultPage = userService.getUserPagingList(searchDto);
// 개인정보조회 이력 남김
userService.writeUserHistoryLog(resultPage);
return resultPage;
}
@Operation(summary = "사용자 조회 (by EP ID)")
@GetMapping("/auth/users/{userId}")
public User userInfo(@Parameter(description = "EP ID", required = true) @PathVariable(required = true) String userId) {
User userInfo = userService.getUserById(userId);
// 개인정보조회 이력 남김
userService.writeUserHistoryLog(userInfo);
return userInfo;
}
----
.UserServiceImpl.class
[source,java]
----
@Override
public void writeUserHistoryLog(Object returnValue) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String requestUri = request.getRequestURI();
String requestMethod = request.getMethod();
User user = Account.currentUser();
if(ObjectUtils.isNotEmpty(user)) { // 로그인된 사용자
try {
HistoryLog log = new HistoryLog();
log.setLogId(idGenService.getNextStringId());
log.setNodeId(nodeId);
if(ObjectUtils.isNotEmpty(user)) {
log.setWorkerId(user.getUserId());
log.setWorkerName(user.getUserName());
}
log.setWorkDatetime(DateTime.now().toString());
log.setRemoteAddr(webUtil.getClientIp(request));
log.setRequestMethod(requestMethod);
log.setRequestUri(requestUri);
log.setApiResult(returnValue);
String jsonVal = mapper.writeValueAsString(log);
USER_HISTORY_LOG.info(jsonVal);
} catch (JsonProcessingException e) {
log.warn(e.getMessage());
}
}
}
----
.log4j2.xml
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<RollingFile name="UserHistoryAppender" fileName="/logs/history/user-history-${date:yyyy-MM-dd}-${hostName}.log"
filePattern="/logs/history/user-history-%d{yyyy-MM-dd}-${hostName}.log">
<PatternLayout>
<Pattern>%d %-5p [%t] %-17c{2} \(%13F:%L\) - %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="UserHistoryLog" level="INFO" additivity="false">
<AppenderRef ref="UserHistoryAppender"/>
</Logger>
</Loggers>
</Configuration>
----
@@ -0,0 +1,195 @@
= 개인정보 취급자 권한변경이력
== 개요
역할 및 업무그룹의 권한 정보를 변경한 이력을 남긴다.
=== 역할 권한 변경 이력 로깅
* AOP를 이용하여 RoleService의 사용자 역할 권한 추가/수정/삭제 메서드가 호출될때 이력을 남긴다.
* log4j2.xml에 설정한 파일에 이력이 남는다.
.RoleHistoryLoggingAspect.class
[source,java]
----
@Aspect
@Component
@Log4j2
public class RoleHistoryLoggingAspect extends HistoryLoggingSupport{
private static final Logger ROLE_HISTORY_LOG = LogManager.getLogger("RoleHistoryLog");
@Value("${node-id}")
private String nodeId;
private final IdGenService idGenService;
public RoleHistoryLoggingAspect(IdGenService idGenService) {
this.idGenService = idGenService;
}
@Pointcut("execution(* com.samsung.role.impl.RoleServiceImpl.insertUserRoleList(..))")
public void insertUserRolePointcut() {
// Do nothing because pointcut
}
@Pointcut("execution(* com.samsung.role.impl.RoleServiceImpl.updateUserRoleList(..))")
public void updateUserRolePointcut() {
// Do nothing because pointcut
}
@Pointcut("execution(* com.samsung.role.impl.RoleServiceImpl.deleteUserRoleList(..)) || execution(* com.samsung.role.impl.RoleServiceImpl.deleteUserRole(..))")
public void deleteUserRolePointcut() {
// Do nothing because pointcut
}
@After(value = "insertUserRolePointcut() || updateUserRolePointcut() || deleteUserRolePointcut()")
public void writeRoleHistoryLog() {
writeHistoryLog(ROLE_HISTORY_LOG, idGenService, nodeId);
}
}
----
.HistoryLoggingSupport.class
[source,java]
----
public class HistoryLoggingSupport {
private static final ObjectMapper mapper = new ObjectMapper();
@Autowired
protected WebUtil webUtil;
public void writeHistoryLog(Logger logger, IdGenService idGenService, String nodeId) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String requestUri = request.getRequestURI();
String requestMethod = request.getMethod();
User user = Account.currentUser();
if(ObjectUtils.isNotEmpty(user)) { // 로그인된 사용자
try {
HistoryLog log = new HistoryLog();
log.setLogId(idGenService.getNextStringId());
log.setNodeId(nodeId);
if(ObjectUtils.isNotEmpty(user)) {
log.setWorkerId(user.getUserId());
log.setWorkerName(user.getUserName());
}
log.setWorkDatetime(DateTime.now().toString());
log.setRemoteAddr(webUtil.getClientIp(request));
log.setRequestMethod(requestMethod);
log.setRequestUri(requestUri);
String jsonVal = mapper.writeValueAsString(log);
logger.info(jsonVal);
} catch (JsonProcessingException e) {
logger.warn(e.getMessage());
}
}
}
}
----
.log4j2.xml
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<RollingFile name="RoleHistoryAppender" fileName="/logs/history/role-history-${date:yyyy-MM-dd}-${hostName}.log"
filePattern="/logs/history/role-history-%d{yyyy-MM-dd}-${hostName}.log">
<PatternLayout>
<Pattern>%d %-5p [%t] %-17c{2} \(%13F:%L\) - %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="RoleHistoryLog" level="INFO" additivity="false">
<AppenderRef ref="RoleHistoryAppender"/>
</Logger>
</Loggers>
</Configuration>
----
=== 업무그룹 권한 변경 이력 로깅
* AOP를 이용하여 WorkGroupService서비스의 업무그룹 권한 추가/수정/삭제 메서드가 호출될때 이력을 남긴다.
* log4j2.xml에 설정한 파일에 이력이 남는다.
.WorkgroupHistoryLoggingAspect.class
[source,java]
----
@Aspect
@Component
@Log4j2
public class WorkgroupHistoryLoggingAspect extends HistoryLoggingSupport {
private static final Logger WORKGROUP_HISTORY_LOG = LogManager.getLogger("WorkgroupHistoryLog");
@Value("${node-id}")
private String nodeId;
private final IdGenService idGenService;
public WorkgroupHistoryLoggingAspect(IdGenService idGenService) {
this.idGenService = idGenService;
}
@Pointcut("execution(* com.samsung.workgroup.impl.WorkgroupServiceImpl.insertWorkgroupRoleList(..))")
public void insertWorkgroupRoleList() {
// Do nothing because pointcut
}
@Pointcut("execution(* com.samsung.workgroup.impl.WorkgroupServiceImpl.insertWorkgroupMenuList(..))")
public void insertWorkgroupMenuList() {
// Do nothing because pointcut
}
@Pointcut("execution(* com.samsung.workgroup.impl.WorkgroupServiceImpl.updateWorkgroupRoleList(..))")
public void updateWorkgroupRoleList() {
// Do nothing because pointcut
}
@Pointcut("execution(* com.samsung.workgroup.impl.WorkgroupServiceImpl.updateWorkgroupMenuList(..))")
public void updateWorkgroupMenuList() {
// Do nothing because pointcut
}
@Pointcut("execution(* com.samsung.workgroup.impl.WorkgroupServiceImpl.deleteWorkgroupRoleList(..))")
public void deleteWorkgroupRoleList() {
// Do nothing because pointcut
}
@Pointcut("execution(* com.samsung.workgroup.impl.WorkgroupServiceImpl.deleteWorkgroupMenuList(..))")
public void deleteWorkgroupMenuList() {
// Do nothing because pointcut
}
@After(value = "insertWorkgroupRoleList() || insertWorkgroupMenuList() || updateWorkgroupRoleList() || updateWorkgroupMenuList() || deleteWorkgroupRoleList() || deleteWorkgroupMenuList()")
public void writeWorkgroupHistoryLog() {
writeHistoryLog(WORKGROUP_HISTORY_LOG, idGenService, nodeId);
}
}
----
.log4j2.xml
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<RollingFile name="WorkgroupHistoryAppender"
fileName="/logs/history/workgroup-history-${date:yyyy-MM-dd}-${hostName}.log"
filePattern="/logs/history/workgroup-history-%d{yyyy-MM-dd}-${hostName}.log">
<PatternLayout>
<Pattern>%d %-5p [%t] %-17c{2} \(%13F:%L\) - %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="WorkgroupHistoryLog" level="INFO" additivity="false">
<AppenderRef ref="WorkgroupHistoryAppender"/>
</Logger>
</Loggers>
</Configuration>
----
@@ -0,0 +1,11 @@
= 관리자 IP 관리
== 관리자 IP 관리
관리자용 계정의 IP를 관리
image::ipMgmt.png[]
=== 기능별 설명
- 삭제 : 등록된 계정을 삭제
- 추가 : 관리자용 계정 정보를 등록하기 위해 Row를 추가
- 저장 : 추가된 계정 정보를 저장
@@ -0,0 +1 @@
= 리소스 접근 제어
@@ -0,0 +1,10 @@
= 문자열 암/복호화
== 개요
필요한 부분에 대하여 암/복호화를 적용하고 있다.
=== 해쉬 알고리즘
ID/Password 로그인의 경우 Password에 대하여 해쉬 알고리즘(org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder)을 적용하고 있다.
=== 전자서명
Knox Portal EpTray를 통한 로그인의 경우 전자서명된 ssoData를 시스템에 있는 개인키(Private Key)를 기반으로 검증한다.
@@ -0,0 +1 @@
= 불법탈취(Hijacking) 방지
@@ -0,0 +1,23 @@
= 비밀번호 관리
== 개요
ID/PW 로그인시 사용하는 비밀번호의 변경 및 초기화가 가능하다.
=== 비밀번호 변경
로그인된 상태에서 비밀번호 변경이 가능하다.
image::pwdChange.png[]
=== 비밀번호 초기화
로그아웃된 상태에서 ID/PWD 로그인시 비밀번호 초기화를 할 수 있다. +
이메일로 임시 비밀번호가 발송된다.
[cols=2*a]
|===
|
.로그인 화면
image::pwdReset_01.png[]
|
.비밀번호 초기화 화면
image::pwdReset_02.png[]
|===
@@ -0,0 +1,28 @@
= 약관 관리
== 이용약관동의 관리
이용약관동의관리 화면을 관리한다.
image::termsConditionList.png[]
== 이용약관동의 등록
등록 화면에서 새로운 약관 생성이 가능하다.
구분은 **공통코드**의 `TERMS` 정보를, 언어는 `TERMS_LANG` 정보를 참조한다.
image::termsConditionDetail_Reg.png[]
== 이용약관동의 목록 추가
그룹코드 관리에서 약관동의 목록 추가가 가능하다.
image::termsCodeDetail.png[]
신규 사용자 로그인 시 화면에 아래와 같이 나타난다.
== 신규사용자 이용약관 동의
신규 사용자 로그인 시 화면에 아래와 같이 나타난다.
image::termsCondition_New.png[]