Initial commit
This commit is contained in:
@@ -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>
|
||||
----
|
||||
@@ -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[]
|
||||
Reference in New Issue
Block a user