Session-based Authentication

세션 기반 인증은 웹 애플리케이션에서 사용자를 식별하고 관리하는 가장 전통적이고 널리 사용되는 방법 중 하나이다. 이 메커니즘은 웹의 초창기부터 사용되어 왔으며, 현재도 많은 시스템에서 핵심적인 인증 방식으로 채택되고 있다.

세션 기반 인증의 기본 개념

세션 기반 인증은 HTTP 프로토콜의 무상태(stateless) 특성을 극복하기 위해 개발되었다. HTTP는 기본적으로 각 요청이 독립적이며, 서버가 이전 요청의 컨텍스트를 기억하지 않는다. 이는 사용자가 로그인 상태를 유지하는 것과 같은 지속적인 상태 관리가 필요한 웹 애플리케이션에서 문제가 된다.

세션 기반 인증의 핵심 아이디어는 다음과 같다:

  1. 사용자가 로그인하면 서버에 세션이라는 임시 상태 저장소가 생성된다.
  2. 서버는 이 세션을 고유하게 식별할 수 있는 세션 ID를 생성한다.
  3. 이 세션 ID는 클라이언트(브라우저)에 쿠키로 저장된다.
  4. 이후 사용자가 요청을 보낼 때마다 이 세션 ID가 함께 전송되므로, 서버는 사용자를 식별할 수 있다.
  5. 로그아웃하거나 세션이 만료되면 서버에서 세션 정보가 삭제된다.

세션 기반 인증의 작동 방식

세션 기반 인증의 상세한 작동 프로세스는 다음과 같다:

인증 단계

  1. 사용자 로그인 시도: 사용자가 자신의 자격 증명(일반적으로 사용자 이름과 비밀번호)을 웹 애플리케이션에 제출한다.
  2. 자격 증명 검증: 서버는 제출된 자격 증명을 검증한다(데이터베이스에 저장된 정보와 비교).
  3. 세션 생성: 자격 증명이 유효하면, 서버는 새로운 세션을 생성하고 고유한 세션 ID를 할당한다.
  4. 세션 저장: 서버는 세션 정보(사용자 ID, 권한, 기타 필요한 데이터)를 메모리, 데이터베이스 또는 캐시 시스템에 저장한다.
  5. 세션 ID 전달: 서버는 HTTP 응답의 Set-Cookie 헤더를 통해 세션 ID를 클라이언트에 전송한다.
  6. 쿠키 저장: 브라우저는 이 세션 ID를 쿠키로 저장한다.

인증 상태 유지 단계

  1. 후속 요청: 사용자가 애플리케이션에 추가 요청을 보낸다.
  2. 쿠키 전송: 브라우저는 자동으로 저장된 세션 쿠키를 요청 헤더에 포함시킨다.
  3. 세션 검증: 서버는 받은 세션 ID를 사용하여 저장된 세션 정보를 조회한다.
  4. 권한 확인: 세션이 유효하면, 서버는 요청을 처리하고 사용자의 권한에 따라 적절한 응답을 반환한다.

세션 종료 단계

  1. 로그아웃 요청: 사용자가 명시적으로 로그아웃을 요청하거나, 세션 타임아웃이 발생한다.
  2. 세션 삭제: 서버는 저장소에서 해당 세션 정보를 삭제한다.
  3. 쿠키 무효화: 서버는 클라이언트의 세션 쿠키를 무효화하도록 지시한다(일반적으로 만료 시간을 과거로 설정).

세션 저장소 옵션

세션 데이터를 저장하는 방법은 애플리케이션의 요구 사항과 아키텍처에 따라 달라질 수 있다.

주요 저장소 옵션은 다음과 같다:

  1. 메모리 저장소
    • 특징: 애플리케이션 서버의 메모리에 세션 데이터를 저장한다.
    • 장점: 접근 속도가 매우 빠르며, 구현이 간단하다.
    • 단점: 서버가 재시작되면 모든 세션 데이터가 손실된다. 다중 서버 환경(로드 밸런싱)에서는 세션 공유 문제가 발생할 수 있다.
    • 사용 사례: 개발 환경, 단일 서버 애플리케이션, 세션 데이터가 중요하지 않은 경우.
  2. 데이터베이스 저장소
    • 특징: 관계형 또는 NoSQL 데이터베이스에 세션 데이터를 저장한다.
    • 장점: 영구적인 저장이 가능하며, 서버 재시작 후에도 세션이 유지된다. 다중 서버 환경에서도 세션 공유가 가능하다.
    • 단점: 메모리 저장소보다 접근 속도가 느리며, 데이터베이스 연결 및 쿼리 오버헤드가 발생한다.
    • 사용 사례: 운영 환경, 세션 데이터가 중요한 애플리케이션, 다중 서버 환경.
  3. 분산 캐시 시스템
    • 특징: Redis, Memcached와 같은 인메모리 캐시 시스템에 세션 데이터를 저장한다.
    • 장점: 메모리 접근 속도에 가까운 성능을 제공하면서도, 다중 서버 환경에서 세션 공유가 가능하다. 일부 시스템은 영구 저장도 지원한다.
    • 단점: 추가적인 인프라 구성이 필요하며, 관리 복잡성이 증가한다.
    • 사용 사례: 대규모 웹 애플리케이션, 고성능이 요구되는 환경, 마이크로서비스 아키텍처.

세션 기반 인증 구현 예제

다음은 Node.js와 Express 프레임워크를 사용한 세션 기반 인증의 기본적인 구현 예제:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');

// 간단한 사용자 데이터베이스 (실제로는 진짜 DB를 사용해야 함)
const users = [
  {
    id: 1,
    username: 'user1',
    // 비밀번호: 'password123'의 해시값
    passwordHash: '$2b$10$aBcDeFgHiJkLmNoPqRsTuVwXyZ123456789abcdefghijklm' 
  }
];

const app = express();

// 미들웨어 설정
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 세션 설정
app.use(session({
  secret: 'your-secret-key', // 세션 ID 쿠키 서명에 사용할 비밀 키
  resave: false, // 세션이 수정되지 않았어도 다시 저장할지 여부
  saveUninitialized: false, // 초기화되지 않은 세션을 저장소에 저장할지 여부
  cookie: {
    secure: process.env.NODE_ENV === 'production', // HTTPS에서만 쿠키 전송 (프로덕션 환경)
    httpOnly: true, // 자바스크립트에서 쿠키에 접근할 수 없도록 함
    maxAge: 1000 * 60 * 60 // 쿠키 유효 기간: 1시간
  }
}));

// 로그인 라우트
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  
  // 사용자 검색
  const user = users.find(u => u.username === username);
  if (!user) {
    return res.status(401).json({ message: '잘못된 사용자 이름 또는 비밀번호' });
  }
  
  // 비밀번호 검증
  const passwordMatch = await bcrypt.compare(password, user.passwordHash);
  if (!passwordMatch) {
    return res.status(401).json({ message: '잘못된 사용자 이름 또는 비밀번호' });
  }
  
  // 세션에 사용자 정보 저장 (비밀번호 해시는 제외)
  req.session.user = {
    id: user.id,
    username: user.username
  };
  
  return res.json({ message: '로그인 성공' });
});

// 인증이 필요한 라우트를 위한 미들웨어
const requireAuth = (req, res, next) => {
  if (!req.session.user) {
    return res.status(401).json({ message: '인증이 필요합니다' });
  }
  next();
};

// 보호된 라우트
app.get('/profile', requireAuth, (req, res) => {
  return res.json({ user: req.session.user });
});

// 로그아웃 라우트
app.post('/logout', (req, res) => {
  req.session.destroy(err => {
    if (err) {
      return res.status(500).json({ message: '로그아웃 중 오류가 발생했습니다' });
    }
    res.clearCookie('connect.sid'); // express-session의 기본 쿠키 이름
    return res.json({ message: '로그아웃 성공' });
  });
});

app.listen(3000, () => {
  console.log('서버가 포트 3000에서 실행 중입니다');
});

이 예제는 기본적인 세션 기반 인증 시스템을 구현하며 다음 기능을 포함한다:

세션 기반 인증의 장단점

장점

  1. 상대적 단순성: 개념적으로 이해하기 쉽고, 많은 웹 프레임워크에서 기본적으로 지원한다.
  2. 서버 측 제어: 서버가 세션을 완전히 제어할 수 있으며, 필요할 때 세션을 즉시 무효화할 수 있다.
  3. 낮은 클라이언트 부담: 클라이언트는 간단한 세션 ID만 저장하고 관리하면 된다.
  4. 유연한 데이터 저장: 서버 측에서 다양한 사용자 관련 데이터를 저장할 수 있다.
  5. 브라우저 호환성: 오래된 브라우저에서도 잘 작동한다.

단점

  1. 서버 상태 의존성: 서버가 모든 활성 세션의 상태를 유지해야 하므로, 서버 메모리 사용량이 증가할 수 있다.
  2. 확장성 문제: 다중 서버 환경에서는 세션 정보를 공유하기 위한 추가적인 메커니즘이 필요하다.
  3. CSRF 취약성: 적절한 보호 없이는 Cross-Site Request Forgery(CSRF) 공격에 취약할 수 있다.
  4. 성능 오버헤드: 요청마다 세션 데이터를 조회하고 검증해야 하므로, 일정한 성능 오버헤드가 발생한다.
  5. 쿠키 의존성: 쿠키 사용이 제한된 환경(일부 모바일 앱 웹뷰 등)에서 문제가 발생할 수 있다.

세션 보안 고려사항

세션 기반 인증의 보안을 강화하기 위해 고려해야 할 주요 사항들은 다음과 같다:

현대적인 세션 관리 접근법

최신 웹 애플리케이션에서는 세션 기반 인증의 전통적인 접근 방식을 개선하기 위한 여러 방법이 사용되고 있다:

사용 사례별 권장 접근법

애플리케이션의 특성에 따라 다른 세션 관리 접근법이 적합할 수 있다:

  1. 전통적인 웹 애플리케이션
    • 권장 접근법: 표준 서버 측 세션(Redis 또는 DB 저장소 사용)
    • 이유: 간단한 구현, 강력한 보안, 즉시 세션 무효화 가능
  2. 마이크로서비스 아키텍처
    • 권장 접근법: 분산 세션 저장소(Redis) 또는 JWTs
    • 이유: 서비스 간 세션 정보 공유 필요성, 확장성 요구 사항
  3. SPA + API 백엔드
    • 권장 접근법: JWT + 리프레시 토큰 또는 세션 쿠키(HttpOnly)
    • 이유: API 중심 아키텍처, 잠재적인 CORS 문제, 클라이언트 측 렌더링
  4. 모바일 앱 + API
    • 권장 접근법: OAuth 2.0 / OpenID Connect 또는 커스텀 토큰 시스템
    • 이유: 쿠키 지원 제한, 앱별 저장소 사용 가능성
  5. 높은 보안이 요구되는 애플리케이션
    • 권장 접근법: 짧은 수명의 서버 측 세션 + 추가 인증 요소
    • 이유: 완전한 세션 제어 필요성, 위험 관리, 규정 준수 요구 사항

용어 정리

용어설명

참고 및 출처