← 프로젝트 회고 · Backend
RETROSPECTIVE · 회고

연속 로그인 실패 횟수는 '마지막 성공 이후'부터 세야 한다

Backend

로그인에 5번 연속 실패하면 계정을 잠그는 기능이 있었다. 그런데 관리자가 잠금을 풀어준 계정이 비밀번호를 한 번만 틀려도 바로 다시 잠기는 문제가 들어왔다.

원인: 잠금 해제가 이력에 남지 않음

연속 실패 횟수를 로그인 이력 테이블에서 "최근 실패 건수"로 세고 있었다. 잠금 해제는 계정 상태 컬럼만 바꾸고 이력에는 아무것도 남기지 않았기 때문에, 해제 전의 실패 5건이 그대로 카운트에 포함됐다. 해제 직후 한 번만 틀려도 6회가 되는 구조였다.

연속 실패의 정의를 바로잡기

"연속"은 마지막 성공(또는 잠금 해제) 이후의 실패다. 그래서 잠금 해제도 하나의 이벤트로 이력에 기록하고, 기준점을 그 시점으로 잡았다.

SELECT COUNT(*)
FROM login_history h
WHERE h.member_id = :memberId
AND h.result = 'FAIL'
AND h.created_at > COALESCE(
(SELECT MAX(created_at)
FROM login_history
WHERE member_id = :memberId
AND result IN ('SUCCESS', 'UNLOCK')),
'1970-01-01')
  1. (member_id, result, created_at) 인덱스가 있어야 이력이 쌓여도 빠르다.
  2. 성공 로그인도 반드시 이력을 남겨야 기준점이 생긴다.

대안: 계정에 실패 카운터 두기

계정 테이블에 fail_count를 두고 실패 시 fail_count = fail_count + 1, 성공·해제 시 0으로 되돌리는 방식도 있다. 조회가 단순하고 원자적 증가라 동시 요청에도 안전하다. 대신 "언제 몇 번 틀렸는지"는 이력 테이블이 따로 필요하다.

같이 챙길 것

  1. 실패 메시지는 "아이디 또는 비밀번호가 올바르지 않습니다"처럼 동일하게 해서 계정 존재 여부가 드러나지 않게 한다.
  2. 영구 잠금 대신 일정 시간 후 자동 해제를 두면 문의가 줄어든다.
  3. 잠금 해제는 누가 했는지까지 이력에 남기면 감사(audit)에도 쓸 수 있다.
Gunmo Lee
연속 로그인 실패 횟수는 '마지막 성공 이후'부터 세야 한다 | Gunmo's Dev Life