안전하고 신뢰할 수 있는 웹 애플리케이션을 구축하기 위한 세션 관리 모범 사례를 살펴보세요.
2025년 2월 19일
2025년 2월 19일
사용자 인증을 마쳤다고 해서 끝났다고 생각한다면, 다시 생각해 보세요. 적절한 세션 관리는 보안과 사용자 경험 측면에서 앱의 성패를 좌우할 수 있습니다. 시작에 도움이 되도록 업계 모범 사례를 모았습니다.

Maria Paktiti

2025년 2월 19일
안전한 세션 관리는 웹 애플리케이션과 사용자를 보호하는 데 매우 중요합니다. 그렇지 않으면 무단 접근, 데이터 유출, 계정 탈취와 같은 보안 취약점에 노출될 수 있습니다. 이러한 위험을 피하고 안전한 환경을 유지하려면 웹 개발자와 시스템 관리자는 주의를 기울이고 흔한 실수를 피해야 합니다.
이 글에서는 세션 관리에 관한 일반적인 모범 사례를 살펴보고, Node 또는 Python을 사용해 각각을 구현하는 방법을 알아보겠습니다.
사용자 세션은 사용자가 앱과 상호작용하는 기간을 의미합니다. 사용자가 로그인하거나 애플리케이션을 열 때 시작하며, 로그아웃하거나 앱을 닫거나 일정 시간 동안 비활성 상태가 되면 종료됩니다.
사용자 세션은 사용자 활동을 추적하고, 사용자 경험을 개선하며, 개인화된 콘텐츠, 사용자 인증, 장바구니 유지 같은 기능을 지원합니다. 각 세션에는 고유 식별자, 일반적으로 세션 ID 또는 토큰이 부여되며, 이는 흔히 사용자의 브라우저에 있는 쿠키에 저장됩니다.
세션 관리 흐름은 다음과 같습니다.
충분히 단순해 보이지만, 주의해야 할 함정이 많습니다. 세션 관리를 구현할 때 기억해야 할 몇 가지 모범 사례를 살펴보겠습니다.
세션 관리의 기본 요소 중 하나는 사용자의 세션을 고유하게 식별하는 세션 식별자(ID)입니다. 세션 ID는 다음 조건을 충족해야 합니다.
무작위성과 예측 불가능성: 예측 가능하거나 순차적인 세션 ID를 사용하면 공격자가 세션 식별자를 추측하거나 무차별 대입하기 쉬워져 세션 보안이 손상됩니다. 항상 충분한 엔트로피를 갖춘 안전한 무작위 알고리즘을 사용해 세션 ID를 생성하세요(예: 128비트 또는 256비트 무작위성).
충분한 길이와 고유성: 짧거나 지나치게 단순한 세션 ID는 충돌 또는 예측 위험을 높입니다. 각 세션 ID의 길이를 충분히 확보하면 이 위험을 최소화할 수 있습니다.
Python으로 안전한 세션 ID를 생성하는 방법:
import secrets
import string
def generate_session_id(length=32):
characters = string.ascii_letters + string.digits
session_id = ''.join(secrets.choice(characters) for _ in range(length))
return session_id
secrets 모듈은 암호화 용도로 명시적으로 설계되었으며 세션 ID나 비밀번호처럼 보안에 민감한 애플리케이션에 적합한 매우 강력한 난수를 생성합니다.
characters 문자열을 수정하면 세션 ID에 사용할 문자 집합(문자, 숫자, 기호)을 쉽게 맞춤 설정할 수 있습니다.
Node로 안전한 세션 ID를 생성하는 방법:
const crypto = require('crypto');
function generateSessionId(length = 32) {
// Generate a secure random session ID (in hexadecimal format)
return crypto.randomBytes(length).toString('hex');
}
// Example usage
const sessionId = generateSessionId();
console.log(`Generated session ID: ${sessionId}`);
crypto.randomBytes(length): 이 함수는 지정한 수의 암호학적으로 안전한 무작위 바이트를 생성합니다.
toString('hex'): 무작위 바이트를 16진수 문자열로 변환합니다. 기본값은 32바이트이며, 64자 길이의 16진수 문자열을 생성합니다.
길이: 기본 길이는 32바이트이지만, 더 길거나 짧은 세션 ID가 필요하면 변경할 수 있습니다.
세션 쿠키는 클라이언트 측에 세션 ID를 저장하는 가장 일반적인 방법입니다. 하지만 한 서비스에서 다른 서비스로 이를 전송할 때는 주의해야 합니다. 세션 쿠키의 가로채기 또는 조작을 방지하려면 다음을 따르세요.
Secure 플래그 설정: 쿠키가 안전한 HTTPS 연결을 통해서만 전송되도록 하여 암호화되지 않은 HTTP에서의 가로채기를 방지합니다.
HttpOnly 플래그 설정: 클라이언트 측 JavaScript가 세션 쿠키에 접근하지 못하게 하여 XSS(교차 사이트 스크립팅) 공격 위험을 줄입니다. HTTPOnly 쿠키는 백엔드 서버만 접근할 수 있습니다.
SameSite 속성 사용: 쿠키가 이를 설정한 도메인으로만 전송되도록 하여 교차 사이트 요청 위조(CSRF) 공격을 방지합니다. 다음 옵션을 사용할 수 있습니다.
Secure 및 HttpOnly 같은 추가 보안 조치가 필요합니다.Expires/Max-Age: 이 속성은 쿠키가 유효한 기간을 정의합니다(로그아웃으로 명시적으로 삭제되지 않는 한). 설정하면 지정된 만료 시간 또는 최대 기간까지 쿠키가 브라우저에 영구적으로 저장됩니다.
Python(Flask)으로 안전한 쿠키를 생성하는 방법:
from flask import Flask, make_response
def set_session():
# Generate a secure session ID using the function of the previous example
session_id = generate_session_id()
# Create a response object
response = make_response("Session cookie set.")
# Set the session_id cookie with Secure, HttpOnly, and SameSite attributes
response.set_cookie(
'session_id',
session_id,
httponly=True, # Ensures the cookie is not accessible via JavaScript
secure=True, # Ensures the cookie is only sent over HTTPS
samesite='Strict', # Prevents the cookie from being sent with cross-site requests
max_age=3600 # The cookie expires in 1 hour
)
return response
Node(Express)로 안전한 쿠키를 생성하는 방법:
const express = require('express');
const app = express();
// Route to set the secure cookie
app.get('/set-session', (req, res) => {
// Generate a secure session ID using the function of the previous example
const sessionId = generateSessionId();
// Set the cookie with the Secure, HttpOnly, and SameSite attributes
res.cookie('session_id', sessionId, {
httpOnly: true, // Ensures the cookie is not accessible via JavaScript
secure: true, // Ensures the cookie is only sent over HTTPS
sameSite: 'Strict', // Prevents the cookie from being sent with cross-site requests
maxAge: 3600000 // The cookie expires in 1 hour
});
res.send('Session cookie set.');
});
응답은 다음과 같은 형태입니다.
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Set-Cookie: session_id=<generated_session_id>; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
Content-Length: 34
Date: Mon, 19 Feb 2025 12:00:00 GMT
Connection: keep-alive
Session cookie set.
세션 탈취 또는 무단 접근 위험을 완화하려면 적절한 세션 만료 및 시간 제한 메커니즘을 구현하는 것이 중요합니다.
짧은 만료 시간 설정: 세션이 무기한 지속되어서는 안 됩니다. 합리적인 비활성 기간 후에 세션이 만료되어야 합니다(예: 15~30분). 사용자가 이 기간 동안 비활성 상태라면 자동으로 로그아웃되어야 합니다.
슬라이딩 만료 구현: 사용자가 세션 동안 활동 중이라면 만료 시간을 연장하세요. 이렇게 하면 정상적인 사용 중 세션 만료를 방지하면서 비활성 사용자에 대한 위험을 줄일 수 있습니다.
고정 기간 후 로그아웃: 세션이 활성 상태더라도 정의된 기간 후에는 결국 만료되어야 합니다(예: 24시간). 이 경우 사용자는 다시 로그인해야 합니다.
여기에 만능 해법은 없으며, 앱의 위험도가 높은지 낮은지에 따라 달라집니다. 은행은 5분 동안 비활성 상태면 로그아웃시키는 반면, 위험도가 낮은 다른 앱은 몇 시간 또는 며칠 동안 로그인 상태를 유지할 수 있습니다. 사용자 경험과 보안 사이의 균형을 맞추면서 자체 요구 사항을 평가해야 합니다.
OWASP 세션 관리 치트 시트는 다음과 같이 권고합니다. “유휴 시간 제한과 절대 시간 제한 값은 모두 웹 애플리케이션과 해당 데이터의 중요도에 크게 좌우됩니다. 일반적인 유휴 시간 제한 범위는 고가치 애플리케이션의 경우 2~5분, 저위험 애플리케이션의 경우 15~30분입니다. 절대 시간 제한은 사용자가 일반적으로 애플리케이션을 사용하는 시간에 따라 달라집니다. 애플리케이션이 사무직 근로자가 하루 종일 사용하도록 의도된 경우, 적절한 절대 시간 제한 범위는 4~8시간일 수 있습니다.”
항상 HTTPS로 세션 트래픽을 암호화하세요. 이렇게 하면 전송 중인 데이터(세션 ID 포함)가 중간자 공격과 도청자로부터 보호됩니다.
세션 고정 공격을 방지하려면 사용자가 로그인 또는 로그아웃에 성공한 후 세션 ID를 재생성해야 합니다.
로그인 후 세션 ID 재생성: 사용자가 로그인하면 공격자가 세션 ID를 탈취하거나 예측하지 못하도록 세션 ID를 재생성해야 합니다.
권한 상승 후 세션 ID 재생성: 사용자의 권한이 변경되는 경우(예: 관리자 접근), 권한 상승 공격을 방지하기 위해 세션 ID를 재생성하세요.
사용자가 로그아웃할 때 세션이 완전히 종료되도록 하세요. 여기에는 세션 데이터를 지우고 세션 ID를 무효화하는 작업이 포함됩니다. Python과 Flask를 사용한 예시는 다음과 같습니다.
from flask import Flask, make_response
app = Flask(__name__)
# Route to clear the session and log out
@app.route('/logout')
def logout():
# Clear the session cookie by setting it to expire immediately
response = make_response("Logged out! Session cookie has been cleared.")
# Clear the session_id cookie
response.delete_cookie(
'session_id',
httponly=True, # Make sure the cookie is securely deleted
secure=True, # Secure flag must match
samesite='Strict' # SameSite must match the initial setting
)
return response
Node와 Express를 사용한 예시는 다음과 같습니다.
// Route to clear the session and log out
app.get('/logout', (req, res) => {
// Clear the session cookie by setting it to expire immediately
res.clearCookie('session_id', {
httpOnly: true,
secure: true, // Make sure to match the secure flag if using HTTPS
sameSite: 'Strict' // SameSite should match the previous setting
});
res.send('Logged out.');
});
이 경로는 res.clearCookie()를 호출하여 세션 쿠키를 지웁니다. 쿠키가 즉시 만료되도록 설정하여 브라우저에 쿠키 삭제를 지시합니다.
쿠키가 올바르게 삭제되도록 동일한 쿠키 속성(httpOnly, secure, sameSite)도 사용합니다.
쿠키가 삭제되면 사용자는 로그아웃됩니다.
비밀번호, 개인 정보 또는 토큰과 같은 민감한 세션 데이터를 세션 내부에 직접 저장하지 마세요. 대신 다음을 따르세요.
최소한의 데이터 저장: 세션 ID 또는 세션 데이터에 대한 참조(예: 사용자 ID, 세션 상태)만 저장하고, 그 외 정보는 데이터베이스나 다른 안전한 소스에서 가져오세요.
서버 측 세션 저장소 사용: 더 높은 보안이 필요한 경우, 특히 민감한 정보는 서버 측에 세션 데이터를 저장하고 쿠키나 로컬 저장소 같은 클라이언트 측 저장소에만 의존하지 마세요.
의심스럽거나 비정상적인 패턴을 탐지할 수 있도록 세션 활동을 지속적으로 모니터링하고 기록하세요.
로그인 위치 추적: 짧은 기간에 서로 다른 지리적 위치에서 로그인하는 것과 같은 이상 징후를 탐지하기 위해 각 세션의 IP 주소와 사용자 에이전트(브라우저/기기 정보)를 기록하세요.
비정상 행동 알림 설정: 여러 번의 로그인 실패 또는 빠른 세션 생성 및 삭제와 같은 의심스러운 활동에 대한 알림을 설정하세요.
정기 감사 수행: 세션 관리 관행을 주기적으로 감사하고 보안 정책을 준수하는지 확인하세요.
WorkOS Radar와 같은 도구는 봇, 불가능한 이동, 무차별 대입 공격 등을 탐지하고 자동으로 차단하거나 보안 팀에 알리는 데 도움을 줄 수 있습니다.
애플리케이션이 부하 분산 시스템을 사용하면 사용자의 요청이 서로 다른 서버에 분산될 수 있으며, 이로 인해 세션 데이터(예: 사용자 환경설정 또는 장바구니 항목) 손실과 같은 문제가 발생할 수 있습니다. 다음 옵션 중 하나를 사용하여 서로 다른 서버 간에도 세션 관리가 원활하게 작동하도록 하세요.
고정 세션: 부하 분산 장치가 보통 쿠키 또는 세션 ID로 세션을 추적하여 사용자의 요청을 동일한 서버로 라우팅합니다. 이는 연속성을 유지하고 더 매끄러운 사용자 경험을 보장하는 데 도움이 됩니다.
공유 세션 저장소: Redis 또는 Memcached 같은 공유 세션 저장소를 사용하면 사용자는 세션 데이터를 잃지 않고 어느 서버로든 라우팅될 수 있습니다. 요청을 어느 서버로든 라우팅할 수 있으므로 더 나은 부하 분산과 확장성이 가능합니다.
JSON 웹 토큰(JWT) 사용: JWT는 상태 비저장 방식으로 설계되었습니다. 즉, 세션 데이터가 토큰 자체에 저장되며 Redis 같은 중앙화된 세션 저장소가 필요하지 않습니다. 각 JWT에는 사용자 인증 데이터, 권한 및 기타 클레임처럼 서버에 필요한 모든 정보가 포함되며, 데이터베이스를 참조하지 않고도 신뢰할 수 있도록 서명됩니다. 여전히 JWT 및 Redis 같은 공유 세션 저장소를 함께 사용해야 할 수 있다는 점에 유의해야 합니다. 이 경우 Redis는 장바구니 항목과 같은 추가 세션 관련 데이터를 저장하는 데 사용하고, JWT는 인증에 사용할 수 있습니다.
JWT를 사용한다면 사용 전에 항상 검증해야 합니다. 여기에는 다음이 포함됩니다.
JWT를 파싱하여 헤더, 페이로드, 서명을 추출합니다.
비밀 키 또는 공개 키를 사용하여 서명을 확인합니다.
JWT가 유효한지 확인하기 위해 만료 시간(exp) 및 사용 가능 시작 시간(nbf) 클레임을 확인합니다.
JWT가 신뢰할 수 있는 주체에 의해 발급되었는지 확인하기 위해 발급자(iss) 클레임을 검증합니다.
JWT가 올바른 수신자를 대상으로 하는지 확인하기 위해 대상(aud) 클레임을 검증합니다.
세션 관리와 직접 관련되지는 않지만, 다단계 인증(MFA)은 추가 검증 계층을 더해 세션 보안을 크게 강화합니다.
사용자가 비밀번호 변경이나 민감한 정보 접근과 같은 민감한 작업을 수행하려 할 때는 두 번째 인증 수단(예: SMS 코드, 인증 앱)을 제공하도록 요청하세요. 앱의 위험도가 높다면 초기 로그인 절차의 일부로 MFA를 요구하는 것을 고려하세요.
온라인에서 사용자를 안전하게 보호하는 일은 보안과 사용성 사이의 끊임없는 균형 싸움입니다. 세션 관리와 관련된 사용자 경험을 개선하려면 다음 팁을 활용하세요.
사용자를 갑자기 로그아웃시키는 대신, 세션이 곧 만료된다는 사실을 미리 알리고 연장할 수 있는 선택지를 제공하세요. 이는 항공사가 항공권을 예약하는 중 한동안 활동이 없을 때 경고하는 방식과 유사합니다. 이렇게 하면 사용자가 예상치 못하게 진행 상황을 잃지 않습니다.
이러한 세션 관리 모범 사례를 구현하는 것은 안전하고 신뢰할 수 있는 웹 애플리케이션을 구축하는 핵심입니다. 강력한 세션 ID를 생성하고, HTTPS를 의무화하며, 쿠키를 올바르게 구성하고, 시간 제한을 적용하고, 세션 ID를 순환시키며, 사용자 세션을 부지런히 모니터링하면 안전한 환경을 유지하고 매끄러운 사용자 경험을 제공할 수 있습니다.