Vikunja에서 세션을 삭제했음에도 실시간 알림이 계속 수신되는 이유는 WebSocket 실시간 푸시 엔드포인트가 인증 과정에서 서버 측의 세션 상태를 확인하지 않기 때문입니다. 세션이 폐기된 후에도 이미 수립된 연결은 무기한으로 유지되며, 심지어 삭제된 세션 토큰을 이용한 새로운 연결 요청까지 수락되는 보안 결함이 발생합니다.
Vikunja WebSocket 세션 폐기 우회 취약점 개요
시스템 운영자와 보안 담당자가 주목해야 할 이 취약점은 Vikunja의 WebSocket 인증 메커니즘이 서버의 세션 관리 테이블과 동기화되지 않아 발생합니다. 일반 HTTP 요청이라면 세션이 삭제될 때 즉시 접근이 차단되지만, WebSocket 엔드포인트는 암호학적으로 유효한 JWT(JSON Web Token)만 확인하고 그 세션의 실제 활성 상태는 무시하는 구조입니다.
이 결함은 GitHub Advisory Database(GHSA-4hv6-xc92-j86g) 및 OSV.dev에 공식 등재되어 그 위험성이 확인됐습니다. 특히 세션 폐기 후에도 라이브 푸시를 지속적으로 수신할 수 있다는 점은 사용자 계정이 탈취되었거나 권한이 회수된 상황에서도 공격자가 실시간 데이터를 계속 가로챈다는 뜻이라 치명적입니다.
기술적 분석: sid 클레임 검증 누락
이 취약점의 핵심은 WebSocket 연결을 처리하는 인증 함수에서 검증 로직이 빠져 있다는 점입니다. 구체적인 기술적 분석 내용을 정리했습니다.
1. 인증 엔드포인트 및 함수 분석
Vikunja의 WebSocket 엔드포인트인 GET /api/v2/ws는 클라이언트가 보낸 첫 메시지를 받았을 때 인증을 수행합니다. 이때 호출되는 Connection.handleAuth 함수(pkg/websocket/connection.go:168)는 전달받은 토큰을 auth.GetUserIDFromToken 함수로 검증합니다.
2. 검증 프로세스의 결함
정상적인 세션 관리라면 토큰 내의 sid(세션 ID) 클레임을 추출하여 서버의 sessions 테이블에 그 세션이 실제로 존재하는지 대조해야 합니다. 하지만 이 함수는 오직 토큰의 암호학적 유효성만 확인하며, sid 클레임을 데이터베이스와 대조하는 단계가 아예 빠져 있습니다.
3. 세션 폐기 메커니즘의 무력화
서버 측에서 세션을 삭제하더라도, 이미 발행된 JWT 토큰 자체가 유효 기간 내에 있고 서명이 올바르다면 WebSocket 엔드포인트는 이를 정상적인 인증으로 간주합니다. 서버의 세션 상태 변경이 WebSocket 연결 상태에 반영되지 않는 ‘세션 폐기 우회’ 현상이 나타나는 셈입니다.
취약점 영향 및 PoC 재현 환경
실제 환경에서 이 취약점이 어떻게 작동하고 어떤 영향을 미치는지 살펴봤습니다.
영향도 분석 및 사례
가장 대표적인 사례는 TOTP(시간 기반 일회용 비밀번호) 활성화 과정입니다. 사용자가 TOTP를 활성화하면 내부적으로 DeleteAllUserSessions 함수가 호출되어 모든 기존 세션이 삭제됩니다. 이론적으로는 모든 기기에서 로그아웃되어야 하지만, WebSocket 경로에서는 이 폐기 효과가 전혀 반영되지 않아 실시간 푸시 알림을 계속 수신하는 보안 허점이 드러납니다.
재현 환경 정보
이 취약점은 실제 공개 PoC(Proof of Concept)로 재현 가능하다는 사실이 확인됐습니다.
| 항목 | 재현 및 확인 내용 |
|---|---|
| 확인 버전 | v2.6.0 |
| 배포 환경 | SQLite 로컬 배포 환경 |
| 취약점 식별자 | GHSA-4hv6-xc92-j86g |
| 결함 현상 | 세션 폐기 후 무기한 라이브 푸시 수신 및 신규 연결 수락 |
대응 방안 및 주의사항
시스템 운영자는 Vikunja의 WebSocket 인증 프로세스가 일반적인 API 요청과 다르게 동작한다는 점을 인지해야 합니다. 서버에서 세션을 삭제하는 것만으로는 이미 연결된 WebSocket 세션을 강제로 종료하거나 새로운 연결을 차단할 수 없습니다.
필요한 보안 관점은 다음과 같습니다.
- 세션 관리 체계 재검토: JWT 기반 인증 시 서버 측 세션 저장소(Session Store)와 실시간으로 대조하는 메커니즘이 구현되어 있는지 확인하십시오.
- 어드바이저리 모니터링: GitHub Advisory Database의 GHSA-4hv6-xc92-j86g 기록을 추적하여 공식 패치 적용 여부를 점검하는 것이 좋습니다.
- 강제 종료 메커니즘 확인: 세션 삭제 시 단순히 DB 레코드를 제거하는 데 그치지 않고, 현재 활성화된 WebSocket 커넥션 리스트를 조회하여 강제로 연결을 끊는 로직이 포함되어 있는지 분석해 볼 필요가 있습니다.
보안 담당자는 특히 TOTP 설정 변경이나 비밀번호 변경처럼 모든 세션을 강제 종료해야 하는 중요 보안 이벤트 발생 시 WebSocket 연결이 여전히 유지되고 있지는 않은지 엄격하게 검증해야 합니다.