들어가며

🔐 JWT와 세션 인증 방식: 무엇을 선택해야 할까?

웹 애플리케이션에서 사용자의 인증을 처리할 때, JWT(Json Web Token)와 세션 인증은 두 가지 주요 방법으로 꼽힙니다. 이 두 방식은 각기 다른 인증 메커니즘을 가지고 있으며, 특정 상황에서는 하나가 더 유리할 수 있습니다. 본 글에서는 JWT세션 인증 방식이 어떻게 활용되고 있는지에 대해서 알아보겠습니다.


1️⃣ JWT(Json Web Token) 인증 방식

✅ 정의

JWT는 서버에 저장하지 않고 클라이언트에서 인증 정보를 관리하는 방식입니다. 클라이언트가 로그인 시, 서버는 JWT를 생성하여 클라이언트에게 전달하고, 클라이언트는 이를 로컬 스토리지 또는 세션 스토리지에 저장하여 이후 요청 시마다 토큰을 서버로 전송합니다.

JWT는 비대칭 암호화로 서명되어 있어, 서버는 JWT를 검증하여 클라이언트의 유효성을 확인합니다.

🚀 JWT 인증 과정

  1. 사용자 로그인: 사용자가 로그인하면, 서버는 JWT를 생성하여 사용자에게 전달합니다. 이 토큰은 사용자의 정보(예: 아이디, 권한 등)를 담고 있으며, 서버에서 비밀 키로 서명합니다.
  2. 토큰 저장: 클라이언트는 받은 JWT를 로컬 스토리지나 세션 스토리지에 저장하고, 이후 요청 시 Authorization 헤더에 포함하여 서버로 전송합니다.
  3. 서버에서 토큰 검증: 서버는 토큰을 확인하고, 서명이 유효한지, 만료되지 않았는지 등을 검증합니다. 검증이 완료되면 사용자의 요청을 처리합니다.

JWT 토큰을 저장하는 방법으로는 크게 두 가지가 있습니다: LocalStorageCookie.

  1. LocalStorage: 클라이언트 측에서 로컬 스토리지에 JWT를 저장하는 방법입니다. 하지만 XSS(교차 사이트 스크립팅) 공격에 취약하므로 LocalStorage에 토큰을 저장하는 것은 안전하지 않습니다. 이는 LocalStorage에 저장된 데이터가 JavaScript를 통해 접근 가능하기 때문입니다.

    XSS 공격을 통해 악의적인 스크립트가 LocalStorage에 저장된 JWT를 탈취할 수 있습니다. 따라서 LocalStorage에 JWT를 저장하는 방식은 보안상 권장되지 않습니다.

  2. Cookie(권장 사용): 쿠키는 서버와 클라이언트 간에 데이터를 저장하고 주고받을 수 있는 방법입니다. JWT를 HTTPOnly 쿠키로 저장하는 방식이 가장 안전합니다. HTTPOnly 쿠키는 JavaScript에서 접근할 수 없으므로 XSS 공격을 방어할 수 있습니다.

    또한 Secure 플래그를 사용하면 HTTPS 환경에서만 쿠키가 전송되므로 보안성이 더욱 강화됩니다.

    💡 JWT를 쿠키로 보내는 방법
    다음과 같이 Set-Cookie 헤더를 사용하여 서버에서 JWT를 클라이언트로 전달합니다:

    Set-Cookie: token=<jwt_token>; HttpOnly; Secure; SameSite=Strict
    • HttpOnly: 클라이언트에서 JavaScript가 쿠키를 접근할 수 없도록 설정
    • Secure: HTTPS를 통해서만 쿠키가 전송되도록 설정
    • SameSite: 외부 사이트에서 쿠키가 전송되지 않도록 설정

🌟 장점

  • Stateless: JWT는 서버에서 세션 정보를 저장하지 않기 때문에 서버가 상태를 유지하지 않습니다. 이로 인해 서버의 부담을 크게 줄일 수 있습니다.
  • 확장성: 클라이언트가 토큰을 관리하기 때문에 서버가 상태를 관리할 필요가 없어서, 여러 서버 간의 확장성에 유리합니다.
  • 멀티 플랫폼 지원: JWT는 기본적으로 JSON 형식으로 데이터를 전달하기 때문에 다양한 플랫폼(모바일, 웹 등)에서 쉽게 사용할 수 있습니다.

⚠️ 단점

  • 토큰 유출 위험: 클라이언트에서 JWT를 저장하고 관리하기 때문에, 만약 토큰이 유출되면 공격자가 인증을 우회할 수 있습니다.

    • 보안 강화 필요: XSS 공격을 방지하려면 HTTPS와 쿠키 속성에 HttpOnly, Secure를 설정하는 등의 방법이 필요합니다.
  • 토큰 만료 후 갱신 어려움: JWT는 보통 만료 시간이 설정되어 있기 때문에, 만약 만료된 JWT를 갱신하는 로직이 필요합니다. 리프레시 토큰을 사용해 만료된 JWT를 갱신할 수 있습니다.

    리프레시 토큰이란? 리프레시 토큰(Refresh Token)은 JWT가 만료되었을 때 새로운 액세스 토큰을 발급받기 위해 사용하는 긴 유효기간을 가진 토큰입니다. JWT는 보통 유효기간이 짧기 때문에 리프레시 토큰을 사용하여 만료된 JWT를 갱신할 수 있습니다.

    🔄 리프레시 토큰의 사용법:

    1. 사용자 로그인: 사용자 로그인 시 서버는 액세스 토큰리프레시 토큰을 함께 발급합니다.
    2. 액세스 토큰 만료: 클라이언트는 액세스 토큰이 만료되면 리프레시 토큰을 서버에 보내 새로운 액세스 토큰을 요청합니다.
    3. 리프레시 토큰 검증 및 새로운 액세스 토큰 발급: 서버는 리프레시 토큰을 검증하고 유효하면 새로운 액세스 토큰을 발급하여 클라이언트에 반환합니다.
      • 리프레시 토큰은 보안이 중요한 자원인 만큼, HTTPOnly 쿠키에 저장하는 것이 안전합니다.
  • 서버 검증 필요: 각 요청에서 토큰을 검증해야 하기 때문에, 서버에 매번 검증 작업이 필요하며, 이는 처리 성능에 영향을 줄 수 있습니다.

예시 코드 (Node.js + Express)

const jwt = require('jsonwebtoken');

// 로그인 후 JWT 발급
function login(req, res) {
  const user = { id: 1, name: "John Doe" }; // 예시 사용자 정보
  const token = jwt.sign(user, 'secretKey', { expiresIn: '1h' }); // 토큰 생성
  res.json({ token });
}

// 미들웨어로 토큰 검증
function authenticate(req, res, next) {
  const token = req.header('Authorization').replace('Bearer ', '');
  try {
    const decoded = jwt.verify(token, 'secretKey'); // 토큰 검증
    req.user = decoded; // 요청 객체에 사용자 정보 저장
    next();
  } catch (err) {
    res.status(401).send('Unauthorized');
  }
}

2️⃣ 세션 인증 방식

✅ 정의

세션 인증 방식은 사용자가 로그인하면 서버에 세션 ID를 저장하고, 클라이언트는 그 ID를 쿠키에 저장하여 이후 요청 시마다 세션 ID를 서버로 전송하는 방식입니다. 서버는 세션 ID를 통해 해당 사용자의 정보를 조회하고 인증을 처리합니다.

🚀 세션 인증 과정

  1. 사용자 로그인: 사용자가 로그인하면, 서버는 사용자 정보를 담은 세션을 생성하고, 클라이언트에 세션 ID를 쿠키로 전달합니다.
  2. 세션 저장: 서버는 이 세션 정보를 메모리나 데이터베이스에 저장하고, 클라이언트는 쿠키에 세션 ID를 저장하여 이후 요청 시 서버로 보내게 됩니다.
  3. 서버에서 세션 검증: 서버는 세션 ID를 통해 사용자 정보를 확인하고, 요청을 처리합니다.

🌟 장점

  • 간편한 관리: 서버에서 모든 세션 정보를 관리하므로, 토큰 만료 및 갱신 문제를 고민할 필요가 없습니다. 세션이 만료되면 서버에서 자동으로 관리됩니다.
  • 세션 상태 관리 용이: 서버에서 세션 정보를 추적하므로, 사용자의 상태를 쉽게 추적할 수 있습니다.
  • 보안성: JWT보다 상대적으로 보안이 강화된 방식입니다. 토큰이 유출되지 않으며, 서버에서 세션을 관리하기 때문에 보다 안전하게 처리할 수 있습니다.

⚠️ 단점

  • 서버 부담: 서버에서 세션 정보를 관리해야 하므로, 사용자가 많아질수록 서버의 부하가 커질 수 있습니다. 특히, 세션 정보를 데이터베이스에 저장하는 경우 DB 부하가 심할 수 있습니다.
  • 확장성 한계: 서버에 세션을 저장하므로, 수평 확장이 어려운 문제가 있습니다. 여러 서버에서 세션을 공유하려면 추가적인 구현이 필요합니다.
  • 세션 스토리지: 세션 ID는 쿠키에 저장되므로, 쿠키가 유출되거나 변조되면 보안 위협이 발생할 수 있습니다. 이를 방지하기 위해 HttpOnly, Secure 쿠키 설정이 중요합니다.

예시 코드 (Node.js + Express)

const express = require('express');
const session = require('express-session');
const app = express();

// 세션 미들웨어 설정
app.use(session({
  secret: 'secretKey', // 비밀키
  resave: false, 
  saveUninitialized: true
}));

// 로그인 후 세션 생성
app.post('/login', (req, res) => {
  req.session.user = { id: 1, name: 'John Doe' }; // 세션에 사용자 정보 저장
  res.send('Logged in');
});

// 인증 확인
app.get('/profile', (req, res) => {
  if (req.session.user) {
    res.send(`Hello, ${req.session.user.name}`); // 세션 정보로 사용자 확인
  } else {
    res.status(401).send('Unauthorized');
  }
});

3️⃣ JWT와 세션 인증의 주요 차이점

특성JWT세션 인증
상태 관리Stateless (서버에 상태를 저장하지 않음)Stateful (서버에 세션 정보를 저장)
서버 부하서버 부담 적음 (토큰은 클라이언트에서 관리)서버 부담 큼 (세션 정보는 서버에서 관리)
확장성서버 확장에 유리서버 확장에 어려움 (세션 공유 필요)
보안토큰 유출 시 위험, 추가 보안 필요서버에서 세션 관리로 비교적 안전
토큰 만료만료된 토큰 갱신 어려움세션 만료 시 자동 관리
클라이언트 저장클라이언트가 토큰을 저장 (로컬 스토리지 등)클라이언트는 세션 ID만 저장 (쿠키)

4️⃣ 사용 시 주의점

JWT 사용 시 주의점

  1. 토큰 만료 처리: JWT는 만료 시간이 설정되어 있기 때문에, 만료된 토큰을 어떻게 처리할지 미리 정의해야 합니다. 일반적으로 리프레시 토큰을 함께 사용하여 JWT 만료 후 새로운 토큰을 발급할 수 있도록 합니다.

  2. 토큰 보안: JWT는 클라이언트 측에서 관리되므로, 반드시 HTTPS를 사용하고, XSSCSRF 공격을 방지하는 보안 조치를 취해야 합니다.

    XSS와 CSRF 공격 & 보안 고려사항

    1. XSS (Cross-Site Scripting: 교차 사이트 스크립팅) 공격 :
      XSS는 악의적인 사용자가 웹 애플리케이션에 악성 스크립트를 삽입하여 클라이언트 측에서 토큰 탈취, 세션 하이재킹 등을 일으킬 수 있는 공격입니다. XSS 공격은 LocalStorage쿠키에 저장된 JWT를 탈취할 수 있습니다. 이를 방지하려면 출력된 데이터를 적절히 필터링해야 합니다.
      1. CSRF (Cross-Site Request Forgery: 교차 사이트 요청 위조) 공격 :
        CSRF는 사용자가 로그인된 상태에서 악성 사이트가 사용자의 권한으로 요청을 보내는 공격입니다. 이를 방지하려면 토큰 기반 인증을 사용하고, 요청 시 CSRF 토큰을 함께 전송하는 방식이 필요합니다. JWT는 이러한 공격에 상대적으로 강한 구조를 가집니다.

    🔐 보안 강화 팁

    • HTTPS 사용: 모든 인증 토큰은 반드시 HTTPS를 통해 전송해야 합니다. HTTP로 전송될 경우, 네트워크 스니핑 공격에 노출될 수 있습니다.
    • XSS 예방: 입력값을 필터링하고, 민감한 데이터는 HttpOnly 속성의 쿠키에 저장하여 XSS 공격을 예방합니다.

      XSS 공격 방어 방법:

      • HTTPOnly 쿠키 사용: 쿠키에 JWT를 저장할 때 HTTPOnly 속성을 추가하여 JavaScript에서 쿠키에 접근할 수 없도록 합니다.
      • 입력값 검증: 모든 사용자 입력을 검증하여 악성 스크립트가 실행되지 않도록 방지합니다.
      • Content Security Policy(CSP): CSP를 사용하여 악성 스크립트가 페이지에 로드되지 않도록 합니다.
    • CSRF 방지: JWT는 CSRF 공격에 강력한 보안 수단이 될 수 있지만, 쿠키 기반 세션을 사용하는 경우 CSRF 토큰을 요청에 포함시켜야 합니다.

      CSRF 공격 방어 방법:

      • CSRF 토큰 사용: 서버는 클라이언트에 고유한 CSRF 토큰을 발급하고, 클라이언트는 요청 시 이 토큰을 함께 보내야 합니다. 서버는 요청에 포함된 CSRF 토큰이 유효한지 검증하여 공격을 방지합니다.
      • SameSite 쿠키 속성 사용: SameSite 속성을 StrictLax로 설정하면 외부 사이트에서 쿠키를 전송할 수 없게 되어 CSRF 공격을 방어할 수 있습니다.
  3. 토큰 크기: JWT에는 많은 정보가 담기기 때문에 토큰 크기가 커질 수 있습니다. 너무 큰 정보를 담지 않도록 주의해야 합니다.

세션 사용 시 주의점

  1. 세션 타임아웃: 세션이 너무 오랫동안 활성화되면 보안상의 위험이 있습니다. 따라서 일정 시간 동안 사용자가 활동하지 않으면 자동으로 세션을 종료시켜야 합니다.

  2. 세션 고정 공격(Session Fixation Attack): 세션 ID가 변하지 않으면 공격자가 세션을 탈취할 수 있습니다. 로그인 후 새로운 세션 ID를 발급하는 방식으로 예방할 수 있습니다.

    🛡️ 세션 고정 공격(Session Fixation Attack) 방어법
    세션 고정 공격은 공격자가 세션 ID를 고정시켜서 피해자가 로그인할 때 이를 사용하게 유도하는 공격입니다. 이렇게 공격자는 피해자의 세션을 탈취할 수 있습니다.

    세션 고정 공격을 방지하는 방법:

    1. 로그인 후 새로운 세션 ID 발급: 사용자가 로그인할 때마다 새로운 세션 ID를 발급하여 기존의 세션 ID가 악용되지 않도록 합니다.
    2. 세션 타임아웃: 일정 시간 동안 사용자가 활동하지 않으면 세션을 종료하여 세션이 장시간 유효하지 않도록 합니다.
  3. 서버 확장성 고려: 세션 기반 인증은 서버 확장 시 어려움을 겪을 수 있습니다. 여러 서버에서 세션을 공유할 수 있는 방안을 고려해야 합니다(예: Redis 활용).


5️⃣ 결론

  • JWT: 확장성서버 부담 감소가 장점이지만, 보안에 유의해야 하고 토큰 관리가 중요합니다. 주로 분산 시스템이나 모바일 앱, 멀티 플랫폼 환경에서 유리합니다.
  • 세션 인증: 보안성이 뛰어나고 관리가 용이하지만, 서버 부하가 크고 확장성에 제약이 있습니다. 작고 단일 서버 환경에서 안정적인 인증 방식으로 사용됩니다.

참고문헌

세션(Session)과 JWT
JWT 로그인방식 구현하기 (feat. session에서 jwt로)
HTTP 세션(session)
사진출처1: Session vs JWT(JSON Web Token)
사진출처2: 토큰 기반 인증 (Token-based Authentication)

참고영상영상


profile
다 먹어버릴거야!

0개의 댓글