Skip to content

fix: 운영 전제가 깨졌는지 코드가 알 수 있게 한다 - #154

Merged
RosieOh merged 1 commit into
mainfrom
fix/ops-preconditions-detectable
Oct 23, 2026
Merged

RosieOh merged 1 commit into
mainfrom
fix/ops-preconditions-detectable

Conversation

@RosieOh

@RosieOh RosieOh commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Refs #90

#90 은 인프라 사실 확인을 요구합니다 — 8082 가 Nginx 에서만 닿는지, Nginx 가 XFF 를 덮어쓰는지, 운영 Redis 가 떠 있는지. 애플리케이션이 알 수 없는 것들입니다. 그중 코드로 막거나 감지할 수 있는 둘을 처리했습니다.

1. 운영에서 리프레시 토큰 저장소가 없으면 기동하지 않는다

store=none 이면 NoopRefreshTokenStore 가 쓰이고 로그아웃이 아무것도 폐기하지 못합니다. 응답은 200 이라 클라이언트는 로그아웃된 것으로 보이지만, 탈취된 리프레시 토큰은 만료(기본 30일) 전까지 계속 새 액세스 토큰을 받아갑니다. 조용히 틀린 상태라 운영에서 알아차릴 방법이 없습니다.

운영 프로파일 기본값은 이미 redis 지만 JWT_REFRESH_TOKEN_STORE 로 덮을 수 있습니다. 그래서 기본값에 기대지 않고 prod 프로파일에서 Noop 이 선택되면 기동을 막습니다. 로그아웃이 동작하지 않는 서버가 뜨는 것보다 뜨지 않는 편이 낫습니다.

로컬·CI 는 Redis 없이 떠야 하므로 그쪽은 그대로입니다("막는 범위가 넓으면 개발이 멈춘다" 도 테스트로 고정).

2. 프록시를 거치지 않은 요청을 지표로 센다

XFF 를 신뢰하도록 설정했다는 것은 "프록시 뒤에 있다" 는 전제입니다. 그런데 XFF 가 없는 요청이 오면 그 요청은 프록시를 거치지 않았습니다 — 즉 앱 포트가 직접 닿는 상태이고, 그러면 공격자가 XFF 를 붙여 IP 기준 제한(로그인·인증코드 발송)을 우회할 수 있습니다.

보안그룹·방화벽은 코드 밖이지만, 이 신호는 런타임에 알 수 있습니다.

carecode.ratelimit.unproxied.requests

0 이 아니면 8082 가 외부에 열려 있다는 뜻입니다. #90 의 첫 체크박스를 배포 후에 확인할 수 있는 방법이 생겼습니다.

  • 로그 경고는 10분에 한 번만 남깁니다 — 직접 노출이면 모든 요청이 해당되어 로그가 쓸려 나갑니다
  • 루프백은 세지 않습니다. 배포 스크립트가 서버 안에서 127.0.0.1:8082 로 헬스체크를 48번까지 돌리므로, 포함하면 배포마다 지표가 튀어 신호가 묻힙니다

이 PR 이 해결하지 못하는 것

세 체크박스는 서버를 봐야 합니다.

항목 확인 방법
8082 가 Nginx 에서만 접근 가능한가 보안그룹 인바운드 규칙. 배포 후 위 지표로 사후 확인도 가능
Nginx 가 XFF 를 덮어쓰는가 proxy_set_header X-Forwarded-For $remote_addr 여야 안전. $proxy_add_x_forwarded_for 면 클라이언트 값이 앞에 붙어 위조값이 첫 항목이 된다
Redis 영속성(AOF/RDB) 끄면 재시작 시 전체 세션이 끊긴다. 감수할지 결정 필요

EMAIL_VERIFICATION_BASE_URL 은 이미 미설정 시 기동 실패(의도된 fail-fast)이고, 배포 검증 단계에서 걸리므로 운영은 무사합니다.

숫자

  • 전체 테스트 666개, 실패 0

#90 은 인프라 사실 확인을 요구한다 — 8082 가 Nginx 에서만 닿는지, Nginx 가 XFF 를
덮어쓰는지, 운영 Redis 가 떠 있는지. 애플리케이션이 알 수 없는 것들이다. 그중 코드로
막거나 감지할 수 있는 두 가지를 처리했다.

1) 운영에서 리프레시 토큰 저장소가 없으면 기동하지 않는다

store=none 이면 NoopRefreshTokenStore 가 쓰이고 로그아웃이 아무것도 폐기하지 못한다.
응답은 200 이라 클라이언트는 로그아웃된 것으로 보이지만, 탈취된 리프레시 토큰은
만료(기본 30일) 전까지 계속 새 액세스 토큰을 받아간다. 조용히 틀린 상태라 운영에서
알아차릴 방법이 없다.

운영 프로파일 기본값은 이미 redis 지만 JWT_REFRESH_TOKEN_STORE 로 덮을 수 있다. 그래서
기본값에 기대지 않고 prod 프로파일에서 Noop 이 선택되면 기동을 막는다. 로그아웃이 동작
하지 않는 서버가 뜨는 것보다 뜨지 않는 편이 낫다.

2) 프록시를 거치지 않은 요청을 지표로 센다

XFF 를 신뢰하도록 설정했다는 것은 "프록시 뒤에 있다" 는 전제다. 그런데 XFF 가 없는
요청이 오면 그 요청은 프록시를 거치지 않았다 — 즉 앱 포트가 직접 닿는 상태이고, 그러면
공격자가 XFF 를 붙여 IP 기준 제한(로그인·인증코드 발송)을 우회할 수 있다.

보안그룹·방화벽은 코드 밖이지만, 이 신호는 런타임에 알 수 있다.
carecode.ratelimit.unproxied.requests 가 0 이 아니면 8082 가 외부에 열려 있다는 뜻이다.
로그 경고는 10분에 한 번만 남긴다 — 직접 노출이면 모든 요청이 해당되어 로그가 쓸려 나간다.

루프백은 세지 않는다. 배포 스크립트가 서버 안에서 127.0.0.1:8082 로 헬스체크를 48번까지
돌리므로, 포함하면 배포마다 지표가 튀어 신호가 묻힌다.

- 전체 테스트 666개, 실패 0

Refs #90
@RosieOh
RosieOh merged commit 1191b74 into main Oct 23, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant