본문 바로가기

보안/시큐어코딩

[시큐어코딩실습] 로그인 시도 횟수 제한

로그인 시도 횟수 제한시 고려해야 할 항목들을 정리해 보면,


- 로그인 시도 횟수 : 로그인 시도시 마다 1씩 증가 시켜, 시도 횟수가 5보다 큰 경우 일정 시간 동안 

                             로그인 기능이 잠금 상태가 되도록 한다.

- 마지막으로 실패한 로그인 시간 정보  : 이 시간 정보를 이용하여 로그인 잠금 상태가 유지 되어야 하는 

                             시간을 설정 할 수 있다.

- 마지막으로 성공한 로그인 시간 정보 : 이 시간 정보를 이용하여 마지막으로 로그인한 시간 정보를 

                             사용자에게 알려줌으로써 자기 계정이 혹시 다른 사람에 의해 사용되었는지 여부를 

                             사용자가 점검 할 수 있도록 한다.

- 로그인한 사용자의 IP 정보 : 이 정보를 관리하므로 해서 다른 IP에서 중복 로그인을 하지 못하도록 설정

                            할 수 있다.  일반적으로 뒤에 로그인되는 사용자에게 중복 로그인에 대한 정보를 제공

                            하고, 이전 로그인 세션의 강제 종료를 설정할 수 있도록 한다. 

                            


 package kr.co.openeg.sec.lab.login.controller;


import java.util.Date;


import javax.annotation.Resource;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;


import kr.co.openeg.sec.lab.login.model.LoginSessionModel;

import kr.co.openeg.sec.lab.login.service.LoginService;

import kr.co.openeg.sec.lab.login.service.LoginValidator;

import kr.co.openeg.sec.lab.member.model.SecurityModel;


import org.springframework.stereotype.Controller;

import org.springframework.validation.BindingResult;

import org.springframework.web.bind.annotation.ModelAttribute;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.servlet.ModelAndView;



@Controller

public class LoginController {

 

@Resource(name="loginService")

private LoginService service;

@RequestMapping("/login.do")

public String login() {

return "/board/login";

}

@RequestMapping(value="/login.do", method = RequestMethod.POST)

public ModelAndView loginProc(@ModelAttribute("LoginModel") 

                                                       LoginSessionModel loginModel, 

                                      BindingResult result, 

                                      HttpServletRequest request, 

                                      HttpSession session) {

ModelAndView mav = new ModelAndView();

// form validation

new LoginValidator().validate(loginModel, result);

if(result.hasErrors()){

mav.setViewName("/board/login");

return mav;

}

String userId = loginModel.getUserId();

SecurityModel sec = service.checkSecurity(userId);

System.out.println("retry: "+sec.getRetry());


                // 로그인 시도횟수가 5회가 넘으면 30초간 로그인 잠금 

if ( sec.getRetry() >= 5 ) {

if  (new Date().getTime() - sec.getLastFailedLogin() < 30000) {

      mav.addObject("userId", userId);

      mav.addObject("errCode", 6);   // 30초동안 로그인잠금 알림

      mav.setViewName("/board/login");

      return mav; 

} else {

sec.setRetry(0);

sec.setLastFailedLogin(0);

service.updateSecurity(sec);

}

String userPw = loginModel.getUserPw();


LoginSessionModel loginCheckResult = 

  service.checkUserId(userId,userPw);

// 로그인이 틀리면, 로그인 시도횟수를 1증가 시키고,

                // 로그인 실패 시간을 DB에 업데이트 한다.

if(loginCheckResult == null){

sec.setRetry(sec.getRetry()+1);

sec.setLastFailedLogin(new Date().getTime());

service.updateSecurity(sec);

mav.addObject("retry",sec.getRetry());

mav.addObject("userId", userId);

mav.addObject("errCode", 1); // not exist userId in database

mav.setViewName("/board/login");

return mav; 

}else {

                // 로그인이 성공하면, 로그인 시도횟수를 0로 reset,

                // 마지막으로 로그인 실패 시간 0으로 reset

                // 성공한 클라이언트 IP를 DB에 업데이트,

                // 로그인 성공시간 DB에 업데이트

sec.setRetry(0);

sec.setLastFailedLogin(0);

sec.setLastSuccessedLogin(new Date().getTime());

sec.setclientIp(request.getRemoteAddr());

service.updateSecurity(sec);

session.setAttribute("userId", userId);

session.setAttribute("userName", loginCheckResult.getUserName());

mav.setViewName("redirect:/board/list.do");

return mav;

}

}

@RequestMapping("/logout.do")

public String logout(HttpSession session){

session.invalidate();

return "redirect:login.do";

}

}


 

소스파일:   LoginController.java



로그인 세션 관리

http://blog.naver.com/kyong94s/119737344