전자금융기반시설 보안 취약점 평가기준 - WEB-SER-045
불충분한 세션 관리 (IS) | 위험도: 4 (높음) | 통제구분: 5.2.4 세션 관리
불충분한 세션 관리 (IS) | 위험도: 4 (높음) | 통제구분: 5.2.4 세션 관리
인터넷뱅킹 로그인 시스템
✅ 로그인 성공
환영합니다, 님
// ❌ 세션 관리 미흡
@PostMapping("/login")
public String login(String userId,
String password,
HttpSession session) {
User user = userService.authenticate(userId, password);
if (user != null) {
// 세션 타임아웃 미설정!
// session.setMaxInactiveInterval(600); // 없음
session.setAttribute("userId", userId);
session.setAttribute("user", user);
// 동시 로그인 체크 안 함!
// 로그아웃 시 세션 무효화 안 함!
return "redirect:/main";
}
return "login";
}
@GetMapping("/logout")
public String logout(HttpSession session) {
// 세션 무효화 안 함!
// session.invalidate(); // 없음
return "redirect:/login";
}
먼저 로그인하거나 공격 시나리오를 선택하세요
// ✅ 안전한 세션 관리
@PostMapping("/login")
public String login(String userId,
String password,
HttpServletRequest request,
HttpSession session) {
User user = userService.authenticate(userId, password);
if (user == null) {
return "login";
}
// 1. 기존 세션 무효화 (세션 고정 공격 방지)
session.invalidate();
session = request.getSession(true);
// 2. 세션 타임아웃 설정 (10분)
session.setMaxInactiveInterval(600);
// 3. 동시 로그인 체크
if (sessionRegistry.isUserLoggedIn(userId)) {
// 기존 세션 강제 종료
sessionRegistry.expireUserSessions(userId);
}
// 4. 세션 등록
session.setAttribute("userId", userId);
session.setAttribute("user", user);
session.setAttribute("loginTime", System.currentTimeMillis());
session.setAttribute("loginIp", request.getRemoteAddr());
// 5. 세션 레지스트리에 등록
sessionRegistry.registerSession(
session.getId(),
userId,
request.getRemoteAddr()
);
// 6. 로그인 이력 저장
auditService.logLogin(userId, request.getRemoteAddr());
return "redirect:/main";
}
@GetMapping("/logout")
public String logout(HttpSession session,
HttpServletRequest request) {
String userId = (String) session.getAttribute("userId");
// 1. 세션 레지스트리에서 제거
sessionRegistry.removeSession(session.getId());
// 2. 세션 완전 무효화
session.invalidate();
// 3. 로그아웃 이력 저장
auditService.logLogout(userId, request.getRemoteAddr());
return "redirect:/login";
}
// 세션 타임아웃 체크 (Filter)
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) {
HttpSession session = ((HttpServletRequest) request)
.getSession(false);
if (session != null) {
long lastAccess = session.getLastAccessedTime();
long now = System.currentTimeMillis();
long inactiveTime = now - lastAccess;
// 10분 비활성 시 세션 무효화
if (inactiveTime > 600000) {
String userId = (String) session.getAttribute("userId");
sessionRegistry.removeSession(session.getId());
session.invalidate();
auditService.logSessionTimeout(userId);
((HttpServletResponse) response)
.sendRedirect("/login?timeout=true");
return;
}
// 마지막 활동 시간 갱신
session.setAttribute("lastActivity", now);
}
chain.doFilter(request, response);
}
// 쿠키 보안 설정
@Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> {
SessionCookieConfig config =
servletContext.getSessionCookieConfig();
// HttpOnly: XSS 방어
config.setHttpOnly(true);
// Secure: HTTPS만 전송
config.setSecure(true);
// SameSite: CSRF 방어
config.setAttribute("SameSite", "Strict");
// 세션 타임아웃
config.setMaxAge(600); // 10분
};
}