package dev.awesomedev.mirim.web;

import jakarta.servlet.http.HttpServletRequest;

/**
 * 이 파일이 하는 일:
 * 요청을 보낸 진짜 클라이언트 IP를 알아낸다 — 로그인 잠금·레이트리밋·감사로그가 공통으로 쓴다.
 *
 * 학습 포인트: 우리 백엔드 앞에는 Caddy(리버스 프록시)가 있다. 그래서
 * request.getRemoteAddr()는 항상 "Caddy의 IP"다 — 이걸 키로 쓰면 전 세계 사용자가
 * 한 명으로 묶여 버린다. 진짜 클라이언트 IP는 Caddy가 X-Forwarded-For 헤더에 담아 준다.
 *
 * ⚠️⚠️ 실제로 났던 취약점 — "맨 앞이 클라이언트"가 함정이었다.
 * X-Forwarded-For는 클라이언트가 임의로 넣어 보낼 수 있는 헤더다. 프록시(Caddy)는
 * 그 값을 지우지 않고 "실제 연결 IP를 뒤에 덧붙인다". 그래서 백엔드가 보는 값은
 *   [클라이언트가 위조한 값] , [Caddy가 본 진짜 IP]
 * 가 된다. 맨 앞(leftmost)을 믿으면 공격자가 요청마다 가짜 IP를 넣어
 * 잠금/레이트리밋 카운트를 매번 새 키로 흩뿌려 방어를 통째로 우회할 수 있었다.
 * → 신뢰할 수 있는 건 "프록시가 직접 덧붙인" 맨 뒤(rightmost) 값이다.
 *   (프록시가 Caddy 하나뿐이라 이 값이 곧 실제 클라이언트 IP다. 프록시가 여러 겹이면
 *    "신뢰하는 프록시 수만큼 뒤에서" 세어야 한다 — 홉 수를 아는 것이 핵심이다.)
 *
 * 학습 포인트 ②: 왜 유틸로 뽑았나 —
 *   이 규칙(rightmost)을 여러 곳에서 각자 구현하면, 한 곳만 leftmost로 잘못 짜도 그 지점이
 *   우회 구멍이 된다. "보안 규칙은 한 곳에만" 두어 모두가 같은 규칙을 쓰게 한다.
 */
public final class ClientIp {

    private ClientIp() {
        // 인스턴스를 만들 이유가 없는 유틸리티 클래스 — 생성 막기.
    }

    public static String of(HttpServletRequest request) {
        String forwarded = request.getHeader("X-Forwarded-For");
        if (forwarded != null && !forwarded.isBlank()) {
            String[] hops = forwarded.split(",");
            return hops[hops.length - 1].trim(); // 맨 뒤 = Caddy가 붙인 실제 IP (위조 불가)
        }
        return request.getRemoteAddr();
    }
}
