세션 기반 인증과 JWT
웹 애플리케이션에서 사용자 인증은 필수적인 요소로, 이것을 구현하기 위한 대표적인 방식으로 세션 기반 인증과 JWT(JSON Web Token) 기반의 인증을 주로 사용합니다.
세션 기반 인증
세션 기반 인증은 서버가 사용자 인증 상태를 유지하는 상태 저장(Stateful) 방식인데요.
사용자가 로그인하면 서버는 세션 ID를 생성하고 이를 클라이언트에 전달합니다.
이후 클라이언트는 요청 시마다 세션 ID를 서버에 함께 전달하고, 서버는 세션 저장소에서 해당 ID를 조회하여 사용자를 인증합니다.
세션 기반 인증의 작동 흐름은 아래와 같이 이루어집니다.
- 사용자가 로그인 요청 (ID와 패스워드)을 서버에 전송합니다.
- 서버는 인증에 성공한 후 세션 ID를 사용자(브라우저)에게 전달합니다.
- 사용자는 이후 요청 시마다 세션 ID를 포함하여 서버에 요청을 보냅니다.
- 서버는 세션 저장소(메모리 DB 등)에서 세션 ID를 조회해서 사용자의 인증 상태를 확인합니다.
세션 기반 인증은 사용자의 세션 및 관련 데이터 정보를 중앙에서 관리하므로 해킹 의심 유저에 대한 세션의 강제 만료가 가능하고 유저 관련 데이터를 보호할 수가 있습니다.
하지만 모든 로그인된 세션에 대한 정보를 서버가 저장하므로 사용자가 많아질수록 서버 부하가 증가하고, 분산 서버 환경에서는 세션의 동기화가 필요하기 때문에 복잡성이 증가할 수 있다는 단점이 있습니다.
세션 기반 인증에서 세션 ID를 단순히 문자열로 전달하는 대신, JSON 형태로 감싸고 암호화하여 토큰 형태로 사용할 수 있는데요.
{
"sessionId": "abcd1234"
}
이러한 형태의 JSON 객체를 AES 등으로 암호화한 후 클라이언트에게 전달하면, 클라이언트는 이를 그대로 요청 시마다 서버에게 전달합니다.
서버는 이 토큰을 복호화한 뒤, 세션 ID를 추출하여 세션 저장소에서 사용자의 인증 상태를 확인합니다.
이러한 방식을 HTTPS와 함께 사용하여 중간자 공격을 방지하고 보안을 강화할 수 있습니다.
중간자 공격(Man-in-the-Middle Attack, MITM)은 공격자가 두 당사자 간의 통신에 몰래 끼어들어 정보를 가로채거나 조작하는 해킹 기법입니다.
JWT 인증
JWT 기반 인증은 서버가 상태를 저장하지 않고, 인증 데이터를 클라이언트가 관리하는 무상태(Stateless) 방식입니다.
JWT는 사용자 정보를 포함한 JSON 객체를 서명하여 생성한 토큰입니다.
서버는 클라이언트가 보낸 JWT를 서명을 통해 검증하여 사용자를 인증합니다.
JWT는 헤더, 페이로드, 서명으로 이루어져 있는데요.
- 헤더: 토큰 타입과 암호화 알고리즘 정보를 포함
- 페이로드: 사용자 정보와 추가 데이터를 포함
- 서명: 서버가 지정한 비밀 키로 생성되어 위변조 여부를 확인
JWT를 사용한 인증은 아래와 같은 프로세스로 이루어집니다.
- 사용자가 로그인 요청(ID와 패스워드)을 보냅니다.
- 서버는 인증에 성공한 후 JWT를 생성하여 사용자(브라우저)에게 전달합니다.
- 사용자는 이후 요청 시 JWT를 포함하여 서버에 요청합니다.
- 서버는 JWT의 서명을 검증하여 인증 상태를 확인합니다.
JWT 방식을 사용하게 되면 상태를 서버에 저장하지 않기 때문에 따로 동기화 등이 필요가 없어 분산 시스템에서 확장성을 염두에 두고 사용하기 좋습니다.
다만 서버에 상태를 저장하지 않기 떄문에 탈취되었을 때 토큰을 무효화하기가 어렵고, 계속 주고받아야 하는 토큰 자체에 사용자 정보와 서명 등을 담고 있기 때문에 네트워크에 부하가 발생할 수 있습니다.
세션 기반 인증과 JWT 기반 인증을 간략하게 표로 정리해보았습니다.
특징 | 세션 기반 인증 | JWT 인증 |
상태 관리 | 서버에서 상태 저장 (Stateful) | 서버에 저장하지 않고 클라이언트에서 상태 관리 (Stateless) |
확장성 | 동기화의 필요 등 서버 부하 증가로 확장성 제한 | 서버 부하 감소로 높은 확장성 |
보안성 | 서버에서 탈취된 세션 무효화 가능 | 탈취되더라도 만료 전까지 사용이 가능 |
데이터 저장 | 서버의 세션 저장소에 저장 | 브라우저에서 토큰 저장 |
구현 복잡도 | 분산 환경에서 동기화 프로세스 필요 | 토큰 관리 및 보안 대책 필요 |
보안 문제와 대응 방안
세션 인증이든 JWT 방식이든, 둘 다 토큰 탈취에 의해 위조된 요청이 보내질 수 있다는 공통적인 보안 문제를 가지고 있습니다.
이러한 위험성을 완전히 예방할 수는 없지만 아래와 같은 대책들로 최대한 방지할 수는 있습니다.
- HTTPS 사용: 모든 통신을 암호화하여 중간 공격 방지
- 짧은 유효 기간: 세션이나 토큰의 만료 시간을 짧게 설정하여 탈취 시 피해를 최소화
- 토큰 저장 방식 개선
- 세션 기반 인증: `HttpOnly` 및 `Secure` 속성이 설정된 쿠키에 저장하여 브라우저의 XSS 공격을 방지
- JWT: 브라우저의 로컬 스토리지보다는 `HttpOnly` 쿠키를 사용
HttpOnly 속성: 이 속성이 설정된 쿠키는 클라이언트 측 스크립트(예: JavaScript)에서 접근할 수 없습니다. 이를 통해 XSS 공격으로부터 쿠키 탈취를 방지할 수 있습니다.
서버는 모든 쿠키를 HTTP 요청의 헤더를 통해 수신하기 때문에 해당 쿠키에 접근하고 활용할 수 있습니다.
Secure 속성: 이 속성이 설정된 쿠키는 HTTPS를 통한 암호화된 연결에서만 전송됩니다. 이를 통해 네트워크 상에서의 중간자 공격을 방지할 수 있습니다.
XSS 공격: 공격자가 웹 페이지에 악성 스크립트를 삽입하여 사용자의 브라우저에서 실행되도록 하는 공격입니다. 이를 통해 쿠키 정보 탈취, 세션 하이재킹 등의 피해가 발생할 수 있습니다.
- IP 및 User-Agent 검증: 요청마다 클라이언트의 IP 주소와 User-Agent를 확인하여 의심스러운 요청을 차단
- 로그아웃 처리
- 세션 기반 인증: 서버에서 세션을 삭제하여 즉시 무효화 가능
- JWT: 토큰 갱신 시 이전 토큰을 블랙리스트에 추가
세션과 JWT 방식은 각각 어떤 상황에서 사용해야 할까?
이론적인 내용을 어느 정도 파악했으니 이제는 그러면 어떻게 적용해야 하는지를 알아야 합니다.
모든 상황에 대해 일괄적으로 적용할 수는 없지만 그래도 큰 틀에서 선택 기준을 정리해 볼 수는 있습니다.
세션 기반 인증이 적합한 경우
1) 보안성이 중요한 시스템
- 사용자 세션을 서버에서 관리하므로 무효화 작업이 수월합니다.
- 금융 서비스나 내부 시스템과 같이 높은 보안이 필요한 경우에 적합합니다.
2) 로그아웃 처리가 필요한 시스템
- 세션 기반 인증은 서버에서 세션을 강제로 만료시킬 수 있으므로 로그아웃 처리가 쉽습니다.
예시를 들자면
- 사내 그룹웨어: 사내 직원들이 내부망에서 사용하는 그룹웨어는 세션 기반 인증이 적합합니다. 로그아웃 처리가 빈번하며, 회사 서버에서 중앙집중적으로 인증 상태를 관리하기 때문에 보안과 유연성을 동시에 확보할 수 있습니다.
- 온라인 쇼핑몰: 장바구니를 유지하거나 로그인 상태를 유지하는 동안, 서버가 세션 저장소에서 상태를 관리하므로 세션 기반 인증이 적합합니다. 장바구니 데이터와 사용자 정보를 일관되게 관리할 수 있습니다.
JWT가 적합한 경우
1) 확장성이 중요한 시스템
- 상태를 서버에 저장하지 않으므로 대규모 사용자 환경에서 효율적입니다.
- 글로벌 서비스난 대규모 트래픽을 처리하는 시스템에 적합합니다.
2) 모바일 및 SPA 환경
- 상태 저장 없이 클라이언트 중심으로 동작하므로 모바일 앱과 싱글 페이지 애플리케이션에 적합합니다.
예시를 들자면
- 모바일 애플리케이션: 모바일 앱은 사용자 인증을 서버와 분리된 상태로 유지해야 하는 경우가 많습니다. 예를 들어, 쇼핑 앱에서 로그인한 상태를 유지하는데 JWT를 활용하면 서버에 부하를 줄이고 확장성도 높일 수 있습니다.
- 마이크로서비스 아키텍처: 여러 서비스가 서로 독립적으로 동작하는 마이크로서비스 환경에서는 JWT가 적합합니다. 토큰에 필요한 인증 정보를 포함하고 각 서비스에서 토큰의 유효성을 독립적으로 확인할 수 있기 때문입니다.
하지만, 여기서 한 가지 의문점이 생겼습니다.
"그러면 모바일 금융 앱이나 모바일 쇼핑 앱도 보안이 상당히 중요한데 그냥 JWT를 사용하는 건가?"
결론적으로 이야기하면, JWT를 사용할 수 있지만 JWT의 단점을 보완하고 추가적인 보안 대책을 적용해야 합니다.
우선 근본적으로 모바일 쪽에서 JWT를 사용하는 이유는
1) 확장성과 성능
- 모바일 금융 앱이나 쇼핑 앱은 대규모 사용자를 처리해야 하는 경우가 많습니다. 이럴 때 모든 세션을 서버에 저장하는 방식을 사용하면 서버에 부하가 너무 커질 수 있습니다.
- JWT는 상태 저장 없이 작동하므로, 서버 부하를 줄이고 분산 시스템에서 확장성을 제공합니다.
2) 오프라인 환경 지원
- JWT는 상태를 클라이언트에 저장하므로, 인터넷 연결이 불안정한 환경에서도 기본적인 인증 정보를 사용할 수 있습니다.
- 이것은 특히 모바일 사용자를 대상으로 하는 앱에서 유용합니다. IoT도 비슷한 이유로 JWT가 유용합니다.
3) 멀티플랫폼 지원
- 모바일 앱, 웹 앱, IoT 기기 등 다양한 플랫폼에서 동일한 인증 메커니즘으로 동작할 수 있습니다.
이러한 여러 가지 장점이 있기 때문에 JWT를 선호하는데요.
하지만 중요한 작업(결제, 송금 등)에 대해서는 MFA(Multi-Factor Authentication)을 통해 추가적인 인증 단계(SMS, OTP 등)를 적용하여 JWT만으로 인증되지 않도록 합니다.
예를 들면
모바일 금융 앱 같은 경우는 액세스 토큰 + 리프레시 토큰 구조를 사용하되, 민감한 데이터를 포함한 요청(송금, 비밀번호 변경, 잔액 조회)에는 추가적인 MFA를 요구합니다.
비슷하게 모바일 쇼핑 앱에서도 결제와 관련된 상황에 대해서는 추가적인 MFA를 요구합니다.
결론적으로, 보안이 중요한 모바일 앱에서도 JWT를 사용할 수 있지만, 보안 대책 없이 단순히 사용하는 것은 적합하지 않습니다.
금융 앱처럼 보안이 최우선인 시스템에서는 세션 기반 인증이 더 적합할 수 있으며, 쇼핑 앱처럼 확장성이 중요한 시스템에서는 JWT를 사용하되, MFA와 같은 추가적인 인증 절차가 필수입니다.
세션 기반 인증과 JWT 기반 인증은 각각의 장단점이 뚜렷하기 때문에, 시스템과 서비스 요구 사항에 따라 적합한 방식을 선택해야 합니다.
세션 기반 인증
- 보안이 중요한 시스템, 로그아웃 처리가 빈번한 환경에서 적합
- 내부 네트워크 서비스나 금융 서비스에 자주 활용
JWT 기반 인증
- 확장성과 분산 환경이 중요한 시스템에 적합
- 모바일 앱, 글로벌 서비스, 마이크로서비스 환경에서 널리 사용
보안 강화의 핵심
- HTTPS를 반드시 사용하여 데이터 탈취를 방지
- 세션 ID나 JWT를 안전한 방식으로 저장 및 전송
- 짧은 만료 시간 설정과 토큰 갱신(Refresh Token) 메커니즘 활용
- 중요한 작업에는 추가적인 인증 절차 추가
결국, 인증 방식은 보안과 확장성의 균형을 맞추는 데 초점을 맞춰 설계되어야 합니다😊