서버 시간 vs 클라이언트 시간: 무엇을 신뢰해야 할까?

근본적인 문제

모든 사용자 기기에는 시계가 있고, 그 시계는 틀릴 수 있습니다. 사용자는 잘못된 시스템 시간, 예상치 못한 시간대를 가질 수 있으며, 악의적인 경우 플래시 세일, 세션 만료, 무료 체험 기간 등 시간 기반 기능을 악용하기 위해 시계를 의도적으로 조작할 수 있습니다.

서버 시간을 사용해야 하는 경우:

  • 레코드 생성 타임스탬프: 이 주문이 언제 접수되었나요? 항상 서버 UTC를 사용하세요.
  • 인증 결정: 세션이 만료되었나요? 쿠폰이 유효한가요? 플래시 세일이 진행 중인가요? 서버 시간만 사용하세요.
  • 감사 로그: 언제 무언가가 발생했는지 신뢰할 수 있는 불변의 기록이 필요할 때.
  • 속도 제한: 이 사용자가 지난 60초 동안 몇 번 요청했나요?

클라이언트 시간을 사용해야 하는 경우:

  • 현지 표시: 사용자에게 현지 시간 표시, 시간대 기반 날짜 선택기.
  • 낮은 위험도 UI 기능: "2분 전" 같은 상대 시간 표시.
  • 시간대 감지: 시간대 기본값 설정을 위한 선호 시간대 읽기.

절대 클라이언트를 신뢰하지 말아야 하는 경우:

# 잘못된 예: 클라이언트 타임스탬프를 레코드 생성에 사용
@app.route("/purchase", methods=["POST"])
def purchase():
    timestamp = request.json["timestamp"]  # 절대 신뢰하지 마세요!
    order = Order(created_at=timestamp)    # 공격자가 임의 값을 설정할 수 있습니다

# 좋은 예: 서버에서 타임스탬프 생성
@app.route("/purchase", methods=["POST"])
def purchase():
    order = Order(created_at=datetime.now(timezone.utc))

플래시 세일과 시간 기반 기능

타이밍이 중요한 기능의 경우: 세일 시작/종료 시간을 서버에서 UTC로 렌더링하고 모든 검증을 서버에서 처리하세요. 클라이언트에서는 카운트다운을 표시하되, 모든 서버 요청에서 항상 다시 검증하세요.