본문 바로가기
C.W.K.
Stream
Lesson 04 of 07 · published

SSL/TLS 와 보안

~13 min · production, security, wss

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

wss:// 는 필수야

운영 WebSocket 은 TLS 위에서 돌아야 해. 많은 사내 방화벽과 프록시가 ws:// 를 차단하고, 평문 연결은 경로에 있는 누구에게나 인증 토큰을 노출해. 구성은 HTTPS 와 같아. 프록시나 애플리케이션에서 TLS 를 종료하고 WebSocket 은 같은 TLS 흐름 위에서 동작하지. Let’s Encrypt 로 무료 인증서를 발급하고 자동 갱신할 수 있어.

보안 점검표

핸드셰이크를 인증 경계로 다뤄. 브라우저가 WebSocket 에는 CORS 를 강제하지 않으므로 Origin 을 검증하고, JWT·쿠키·서명 토큰으로 사용자를 인증해. IP 별·사용자별 속도 제한을 두고 Pydantic 으로 들어오는 모든 메시지를 검증해. 메시지 크기 상한도 Starlette 기본값 16MB 보다 낮추고, 여러 기기는 허용하되 사용자별 연결이 수천 개가 되는 것은 막아.

Code

다층 방어를 적용한 WebSocket 엔드포인트·python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import time

ALLOWED_ORIGINS = {'https://app.example.com', 'https://admin.example.com'}
MAX_MSG_PER_SEC = 20

@app.websocket('/ws')
async def secure_ws(websocket: WebSocket):
    # 1. Origin validation (CORS for WebSocket is your job)
    origin = websocket.headers.get('origin', '')
    if origin not in ALLOWED_ORIGINS:
        await websocket.close(code=4003, reason='forbidden origin')
        return

    # 2. Auth — before accept()
    token = websocket.query_params.get('token')
    user = decode_jwt(token) if token else None
    if not user:
        await websocket.close(code=4001, reason='unauthorized')
        return

    await websocket.accept()

    # 3. Per-connection state for rate limiting
    msg_window = []  # timestamps in the last second

    try:
        async for msg in websocket.iter_json():
            now = time.time()
            msg_window = [t for t in msg_window if now - t < 1.0]
            if len(msg_window) >= MAX_MSG_PER_SEC:
                await websocket.send_json({
                    'type': 'error', 'code': 'rate_limited',
                })
                continue
            msg_window.append(now)

            # 4. Validate every message
            if not isinstance(msg, dict) or 'type' not in msg:
                await websocket.send_json({
                    'type': 'error', 'code': 'invalid_message',
                })
                continue

            await dispatch(websocket, user, msg)
    except WebSocketDisconnect:
        pass

External links

Exercise

기존 WebSocket 엔드포인트를 Origin, 인증, 속도 제한, 메시지 검증, 메시지 크기, 연결 수 상한 점검표에 맞춰 감사해. 각 항목을 0/1/2 로 매겨. 0 은 없음, 1 은 일부, 2 는 완료야. 2 보다 낮은 항목을 모두 고쳐. Caddy 나 Let’s Encrypt 로 wss:// 에 배포하고 ws:// 연결이 거절되는지도 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.