Skip to content

[ Infra ] #80 머지 전 운영 전제 확인 (XFF 신뢰, 리프레시 토큰 저장소) #90

Open

Description

@RosieOh

💚 어떤 기능인가요?

#80 이 운영 프로파일의 기본값 두 개를 바꾼다. 둘 다 인프라 전제가 실제와 맞아야 안전하다.
머지 전에 확인이 필요하다.

1. RATE_LIMIT_TRUST_XFF 를 true 로

프록시 뒤에서 X-Forwarded-For 를 신뢰하지 않으면 모든 요청이 프록시 IP 하나로 집계되어,
IP 기준 제한(로그인·인증코드 발송)이 사실상 전역 카운터가 된다.

반대로 프록시를 거치지 않고 앱 포트(8082)가 직접 노출되면 헤더 위조로 제한을 우회할 수 있다.

  • 운영 인스턴스의 8082 가 Nginx 에서만 접근 가능한지 (보안그룹/방화벽)
  • Nginx 가 X-Forwarded-For 를 덮어쓰는지, 아니면 클라이언트 값을 이어붙이는지
    (이어붙이면 첫 항목이 클라이언트 위조값이 될 수 있다)

2. jwt.refresh-token.store 를 redis 로

none 이면 NoopRefreshTokenStore 가 쓰이고 로그아웃이 아무것도 폐기하지 못한다.
탈취된 리프레시 토큰을 만료(30일) 전까지 회수할 방법이 없다.

  • 운영 Redis 가용성 확인 (rate limit 이 이미 Redis 를 쓰므로 사실상 필수 의존)
  • Redis 재시작 시 전체 세션이 끊기는 것을 감수할지, 영속성(AOF/RDB)을 켤지 결정

3. 인증 메일 기준 주소

  • EMAIL_VERIFICATION_BASE_URL 을 운영 환경변수에 추가 (미설정 시 localhost 로 링크가 나간다)

✅ To Dos

Activity

  1. added this to the Production Readiness milestone on Aug 22, 2026
  2. added theissue type on Aug 22, 2026
  3. RosieOh commented on Aug 22, 2026

    @RosieOh
    ContributorAuthor

    리뷰 결과 — 항목 1(XFF)의 전제가 틀렸다

    머지 전 점검 중 확인한 내용이다. 이 이슈에 처음 적은 근거가 사실과 달랐다.

    실질적인 XFF 신뢰 스위치는 app.rate-limit.* 이 아니다

    application.yml 에 이미 있는 설정이 진짜 스위치다.

    server:
      forward-headers-strategy: framework   # main 에 이미 있음 (#80 이전부터)

    이게 등록하는 ForwardedHeaderFilter 가 요청을 감싸서 getRemoteAddr() 자체를
    X-Forwarded-For 의 클라이언트 주소로 바꿔친다.
    실측 결과다.

    remoteAddr=10.0.0.5, X-Forwarded-For=203.0.113.77
    → 필터 통과 후 getRemoteAddr() = 203.0.113.77
    

    ClientIpResolver 는 trust=false 일 때 getRemoteAddr() 로 폴백하는데,
    그 값이 이미 헤더에서 온 것이라 두 설정이 같은 결과를 낸다.

    ForwardedClientIpTest 로 고정해 뒀다.

    그래서 달라지는 것

    • 이 이슈에 적었던 "trust=false 면 모든 요청이 프록시 IP 하나로 집계된다" 는
      이 앱에서는 성립하지 않는다. forward-headers-strategy 가 켜져 있는 한 클라이언트별로 구분된다.
    • 반대로 "trust=false 니까 헤더 위조에 안전하다" 도 틀렸다. 위조 노출은
      #80 이전부터 이미 존재했고, #80 의 RATE_LIMIT_TRUST_XFF=true 는 새로 만든 위험이 아니다.
    • 따라서 이 항목은 #80 머지를 막지 않는다. 다만 아래 확인은 여전히 필요하다.

    남은 확인 (머지와 무관하게 유효)

    배포가 포트를 모든 인터페이스에 바인딩한다.

    docker run -d --name carecode-$TARGET -p $PORT:8082 ...   # 127.0.0.1: 바인딩 아님

    즉 보안그룹/방화벽이 유일한 방어선이고, 뚫려 있으면 XFF 위조로 IP 기준 제한
    (로그인 시도, 인증코드 발송)이 무력화된다. 이건 #80 과 독립적인 기존 리스크다.

    • 운영 인스턴스의 8082/8083 이 Nginx 에서만 접근 가능한지 확인
    • 아니라면 -p 127.0.0.1:$PORT:8082 로 바인딩을 좁히는 것을 검토
    • Nginx 가 X-Forwarded-For 를 덮어쓰는지(proxy_set_header X-Forwarded-For $remote_addr)
      확인 — 이어붙이면($proxy_add_x_forwarded_for) 첫 항목이 클라이언트 위조값이 될 수 있다
    • 장기적으로 app.rate-limit.trust-forwarded-headers 를 없애고
      forward-headers-strategy 하나로 일원화할지 검토 (지금은 이름이 통제력을 과장한다)

    항목 3 (인증 메일 기준 주소) — 기동 실패로 변경

    #80 에서 운영 기본값을 빈 문자열로 뒀는데, 그러면 링크가 /auth/verify?token=...
    상대 경로로 나가 메일에서 클릭이 안 된다. 사용자는 이유를 모른 채 인증에 실패한다.

    기본값을 제거해 DB_PASSWORD / JWT_SECRET 과 같은 취급으로 바꿨다.
    환경변수가 없으면 기동이 실패한다.

    base-url: ${EMAIL_VERIFICATION_BASE_URL}

    배포 실패는 헬스체크에서 걸리고 블루/그린 전환이 안 되므로 자동 롤백된다 — 안전한 실패다.

    • /opt/carecode/.env 에 EMAIL_VERIFICATION_BASE_URL 추가 (다음 배포 전 필수)

    항목 2 (리프레시 토큰 저장소) — 변경 없음

    redis 유지. rate limit 인터셉터가 이미 모든 요청에서 Redis 를 쓰므로 Redis 는
    사실상 기존 필수 의존이고, 새 의존을 추가하는 것이 아니다.

    • Redis 재시작 시 전체 세션이 끊기는 것을 감수할지, 영속성(AOF/RDB)을 켤지 결정
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions