본문 바로가기
웹개발

차단했는데 12시간을 더 쓴다 — 세션 인증에는 무효화가 없다

by 꼼냥냥 2026. 9. 21.
반응형

계약이 끝난 점포의 계정을 전부 "사용 안 함"으로 바꿨는데, 이미 로그인해 있는 사람은 그대로 쓸 수 있었다. 세션 만료(12시간)까지 정상 사용자다. 버그가 아니라 세션 인증의 성질이다. 로그인할 때 한 번 검증하고 그 뒤로는 세션이 있다는 것만으로 인증된 요청이 된다. 발급한 것을 회수하는 경로는 따로 만들어야 한다. 그리고 세션을 지우려다 엉뚱한 걸 지울 뻔했다. 삭제 기준으로 잡은 속성이 "그 사람이 누구인지"가 아니었다.

세션 인증에는 "무효화"가 없다

로그인 시점에 계정을 검증하고, 통과하면 세션에 사용자 정보를 담는다. 그 다음부터는 세션이 있다는 것만으로 인증된 요청이 된다.

요청마다 하는 검사는 이 정도다.

// 요청한 점포와 세션의 점포가 같은지
if (!requestOffice.equals(sessionOffice)) { ... }

계정이 아직 유효한지는 안 본다. 볼 이유가 없었다. 로그인할 때 확인했으니까.

그래서 계정을 차단해도 이미 발급된 세션은 그대로 산다.

이건 세션 인증의 성질이다

토큰이든 세션이든 한 번 발급하면 회수 경로가 따로 필요하다. JWT를 쓰면 더 심하다. 서버에 상태가 없으니 만료 전에는 손을 못 댄다.

세션은 그나마 낫다. 저장소에 실물이 있으니 지우면 즉시 끊긴다. 우리는 Redis에 담고 있었다.

지우려다 엉뚱한 걸 지울 뻔했다

점포 단위로 끊으려고 세션 속성 중 storeCode로 검색했다. 33개 계정이 있는 점포인데 딱 하나 잡혔다.

그게 내 세션이었다.

이유가 있었다. 내부 운영 계정은 점포를 전환하며 여러 점포를 볼 수 있고, 그때마다 인터셉터가 세션의 storeCode를 조회 중인 점포로 덮어쓴다. 그 점포를 마지막으로 본 사람이 나였다.

그대로 지웠으면 나만 로그아웃되고 정작 대상은 그대로였다.

교훈은 단순하다. 세션 속성은 "그 사람이 누구인지"가 아니라 "지금 무엇을 보고 있는지"일 수 있다. 지우는 기준으로 쓸 값인지 먼저 확인해야 한다.

계정 아이디로 다시 훑었다. 활성 세션 99개를 전부 대조했더니 그 점포 계정은 0건, 차단된 계정도 0건이었다. 차단한 지 오래돼서 이미 다 만료된 것이다.

결국 지울 게 없었다. 하지만 "없다"를 확인한 것과 "있는데 못 찾은 것"은 다르다. 잘못된 기준으로 조회했으면 후자였다.

네임스페이스를 확인하지 않고 조회했다

여기서 한 번 더 틀렸다. 처음 조회했을 때 키가 하나도 안 나왔다. "세션이 없나 보다" 하고 넘어갈 뻔했다.

실제로는 키 접두어가 달랐다. 앱별로 네임스페이스를 나누는 커밋이 있었는데 그게 운영 브랜치에는 안 들어가 있었다. 코드 기준으로 접두어를 넘겨짚은 것이다.

admin:session:sessions:*    →     0건
spring:session:sessions:*   → 8,428건

0건은 "없다"가 아니라 "못 찾았다"일 수 있다. 조회 조건이 맞는지부터 확인해야 한다. 확실히 존재하는 것으로 한 번 돌려 보면 검사 자체가 동작하는지 알 수 있다.

같은 명령이 다른 데서 뭔가를 잡아낼 때만 0이 0이다 — 인프라 쪽에서도 같은 함정을 겪었다. 대조군 없는 0건은 근거가 못 된다.

제대로 고치려면

세션을 지우는 건 사후 처리다. 근본은 요청마다 계정 상태를 다시 보는 것이다.

인터셉터에 한 줄 넣으면 된다.

// 세션의 계정이 아직 유효한지
if (!userService.isActive(sessionUserId)) {
    session.invalidate();
    response.sendRedirect("/login");
    return false;
}

비용이 걸리면 짧은 캐시를 둔다. 1분 캐시여도 12시간보다는 720배 낫다.

우리는 아직 안 넣었다. 이번 건은 오래전에 차단한 계정이라 급하지 않았고, 전체 요청에 조회를 하나 더 다는 판단이 필요해서다. 다만 즉시 차단이 필요한 상황이 오면 지금은 세션을 직접 지우는 수밖에 없다는 걸 알고 있는 것과 모르는 건 다르다.

급할 때 쓰는 방법

계정 기준으로 세션을 찾아 지운다. 점포나 다른 속성이 아니라 계정 아이디로.

KEYS  로 세션 키를 훑고
각 키의 사용자 아이디 속성을 읽어
대상이면 세션 키와 만료 키를 함께 지운다

만료 키를 같이 지우는 걸 잊으면 찌꺼기가 남는다.

운영에서 KEYS는 조심해야 한다. 키가 많으면 블로킹된다. 세션 수가 만 단위면 SCAN으로 나눠 도는 게 맞다.

정리

  • 세션·토큰 인증은 발급 후 회수 경로가 따로 없으면 차단이 즉시 반영되지 않는다.
  • 우리 경우 최대 12시간(세션 만료)까지 차단된 계정이 그대로 쓴다.
  • 세션 속성은 "누구인지"가 아니라 "지금 무엇을 보고 있는지"일 수 있다. 삭제 기준으로 쓰기 전에 확인한다.
  • 조회 결과 0건은 "없다"가 아니라 "못 찾았다"일 수 있다. 네임스페이스·접두어부터 확인한다.
  • 근본 해결은 요청마다 계정 상태를 재검증하는 것. 짧은 캐시를 둬도 12시간보다는 훨씬 낫다.

댓글