전체 그래프
Authentication

Session

web-securityauthenticationsessioncookie

상위: Web Security

요약

Session은 서버가 클라이언트의 상태를 유지하기 위해 사용하는 전통적인 인증 방식입니다. 서버가 세션 데이터를 저장하고, 클라이언트는 세션 ID만 쿠키로 보관합니다.

세션 동작 원리

┌─────────┐                         ┌─────────────┐
 Client                              Server    
└────┬────┘                         └──────┬──────┘
       1. 로그인 요청                      
       (username, password)               
     │────────────────────────────────────▶│
                                          
                        ┌────────────────────────────┐
                         2. 세션 생성                
                            session_id = "abc123"   
                            session_data = {        
                              user_id: 1,           
                              username: "john"      
                            }                       
                             서버 메모리/DB에 저장   
                        └────────────────────────────┘
                                          
       3. 세션 ID 쿠키 설정                 
       Set-Cookie: session_id=abc123      
     │◀────────────────────────────────────│
                                          
       4. 이후 요청                        
       Cookie: session_id=abc123          
     │────────────────────────────────────▶│
                                          
                        ┌────────────────────────────┐
                         5. 세션 조회                
                            sessions["abc123"]      
                             user_id: 1 확인       
                        └────────────────────────────┘
                                          
       6. 인증된 응답                      
     │◀────────────────────────────────────│

세션 저장 방식

1. 메모리 (기본)

# Python Flask
from flask import Flask, session
app.secret_key = 'secret-key'
session['user_id'] = 123

장점: 빠름 단점: 서버 재시작 시 초기화, 확장성 제한

2. 파일 시스템

// PHP
session_save_path('/var/lib/php/sessions');
session_start();
$_SESSION['user_id'] = 123;

3. 데이터베이스

CREATE TABLE sessions (
    session_id VARCHAR(128) PRIMARY KEY,
    user_id INT,
    data TEXT,
    expires_at TIMESTAMP
);

4. Redis (권장)

# Flask-Session + Redis
from flask_session import Session
from redis import Redis

app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='localhost', port=6379)
Session(app)

장점:

  • 여러 서버 간 세션 공유
  • 빠른 읽기/쓰기
  • TTL(만료) 자동 관리

쿠키 보안 설정

Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
속성설명보안 효과
HttpOnlyJavaScript 접근 차단XSS 방어
SecureHTTPS에서만 전송도청 방지
SameSite=Strict같은 사이트에서만 전송CSRF 방어
SameSite=LaxGET 요청은 허용부분 CSRF 방어
Path쿠키 전송 경로 제한범위 제한
Max-Age/Expires만료 시간세션 수명 제한

세션 보안 취약점

1. Session Fixation

공격자가 심어둔 세션 ID를 피해자가 쓰게 유도 → 로그인 시 세션 ID 재생성으로 방어. 상세는 Session Fixation.

2. Session Hijacking

네트워크 스니핑·XSS로 세션 ID를 탈취 → HTTPS·HttpOnly·IP 검증으로 방어. 상세는 Session Hijacking.

3. Session Timeout

오래된 세션이 계속 유효
 공유 PC에서 로그아웃  하면 다음 사용자가 접근

방어:

# 세션 타임아웃 설정
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=30)

# 마지막 활동 시간 추적
@app.before_request
def update_last_activity():
    session['last_activity'] = datetime.utcnow()

@app.before_request
def check_session_timeout():
    if 'last_activity' in session:
        inactive = datetime.utcnow() - session['last_activity']
        if inactive > timedelta(minutes=30):
            session.clear()
            return redirect('/login')

4. Insufficient Session ID Randomness

# 취약: 예측 가능한 세션 ID
session_id = f"user_{user_id}_{timestamp}"

# 안전: 암호학적으로 안전한 랜덤
import secrets
session_id = secrets.token_urlsafe(32)

구현 예시

Python Flask

from flask import Flask, session, redirect, request
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_hex(32)

# 세션 설정
app.config.update(
    SESSION_COOKIE_SECURE=True,     # HTTPS only
    SESSION_COOKIE_HTTPONLY=True,   # No JS access
    SESSION_COOKIE_SAMESITE='Strict',
    PERMANENT_SESSION_LIFETIME=timedelta(hours=1)
)

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']

    user = authenticate(username, password)
    if user:
        session.clear()  # 세션 고정 방지
        session['user_id'] = user.id
        session['ip'] = request.remote_addr
        session.permanent = True
        return redirect('/dashboard')

    return 'Login failed', 401

@app.route('/logout')
def logout():
    session.clear()
    return redirect('/login')

@app.route('/dashboard')
def dashboard():
    if 'user_id' not in session:
        return redirect('/login')
    return f"Welcome, User {session['user_id']}"

Express.js

const session = require('express-session');
const RedisStore = require('connect-redis').default;
const redis = require('redis');

const redisClient = redis.createClient();

app.use(session({
    store: new RedisStore({ client: redisClient }),
    secret: 'your-secret-key',
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: true,       // HTTPS only
        httpOnly: true,     // No JS access
        sameSite: 'strict',
        maxAge: 3600000     // 1 hour
    }
}));

app.post('/login', (req, res) => {
    const { username, password } = req.body;

    if (authenticate(username, password)) {
        // 세션 재생성 (fixation 방지)
        req.session.regenerate((err) => {
            req.session.userId = user.id;
            res.redirect('/dashboard');
        });
    } else {
        res.status(401).send('Login failed');
    }
});

app.get('/logout', (req, res) => {
    req.session.destroy();
    res.redirect('/login');
});

Session vs JWT 선택

시나리오권장
전통적 웹 앱 (서버 렌더링)Session
단일 서버Session
여러 서버 + 공유 저장소Session (Redis)
Stateless APIJWT
모바일 앱JWT
마이크로서비스JWT
즉시 무효화 필요Session

보안 체크리스트

  • 세션 ID는 암호학적으로 안전한 랜덤 값
  • HttpOnly, Secure, SameSite 쿠키 속성 설정
  • 로그인 시 세션 ID 재생성
  • 적절한 세션 타임아웃 설정
  • 로그아웃 시 서버 측 세션 완전 삭제
  • 민감 작업 시 재인증 요구
  • 동시 세션 제한 (선택)
  • IP/User-Agent 바인딩 (선택)

관련 공격

관련 개념

  • JWT - Stateless 토큰 인증
  • OAuth - 위임 인증
  • Cookie - 세션 ID 전송 수단

참고 자료