Client IP 기록

최정환·2024년 11월 3일
0

IP 주소를 기록할 때 프록시 서버를 거치는 경우 X-Forwarded-For와 같은 헤더를 활용하여 원래 클라이언트의 IP를 추적합니다.

그러나 이런 헤더를 사용할 때도 몇 가지 문제점이 발생할 수 있어 이를 개선할 필요가 있습니다.

1. 기존 유틸리티 함수의 문제점

기존 코드(인터넷을 떠도는)에서는 여러 헤더를 순차적으로 검사하여 IP 주소를 가져오는 방식을 사용하고 있습니다.

특히, X-Forwarded-For 헤더가 우선적으로 사용되며, 값이 비어 있거나 “unknown”일 때 다른 헤더를 차례대로 확인하는 방식입니다.


public static String getIp() {
    HttpServletRequest request = getRequest();
    String ip = Objects.requireNonNull(request).getHeader("X-Forwarded-For");
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getHeader("Proxy-Client-IP");
    }
    // 다른 헤더들을 순차적으로 확인
    ...
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getRemoteAddr();
    }
    return ip;
}

문제점 1: X-Forwarded-For 헤더의 다중 IP 주소

X-Forwarded-For는 여러 프록시 서버를 거칠 때 각 프록시가 클라이언트의 IP를 기록하면서 추가되는 구조입니다.

따라서 이 헤더에는 여러 IP 주소가 포함될 수 있으며 각 IP는 쉼표로 구분됩니다. 만약 클라이언트의 실제 IP를 알고 싶다면 첫 번째 IP를 사용해야 합니다.
기존 코드에서는 이를 처리하지 않아 여러 IP가 기록될 가능성이 있습니다.

문제점 2: 코드의 중복과 복잡성

각 헤더를 개별적으로 검사하는 방식은 코드 중복이 많아 가독성과 유지보수에 좋지 않습니다. 또한, “unknown”과 같은 값이 여러 번 반복되어 가독성이 떨어집니다.

2. 개선된 유틸리티 함수

개선된 함수에서는 다음과 같은 방법으로 문제를 해결했습니다:

1.	X-Forwarded-For 헤더가 여러 IP를 포함할 경우 가장 첫 번째 IP만 선택하여 클라이언트의 IP를 정확하게 추출합니다.
2.	나머지 헤더에 대해서는 반복문을 통해 순차적으로 검사하고 첫 번째로 유효한 IP를 반환하도록 하여 코드 중복을 줄였습니다.

public static String getIp() {
    HttpServletRequest request = getRequest();
    String ip = Objects.requireNonNull(request).getHeader("X-Forwarded-For");
    if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
      // 여러 IP가 있는 경우 클라이언트에 가까운 첫 번째 IP만 사용
      ip = ip.split(",")[0].trim();
    } else {
      // X-Forwarded-For가 없는 경우 다른 헤더를 사용
      ip = getFallbackIp(request);
    }
    return ip;
}

private static String getFallbackIp(HttpServletRequest request) {
    String[] headers = {
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_CLIENT_IP",
            "HTTP_X_FORWARDED_FOR",
            "X-RealIP",
            "REMOTE_ADDR"
    };
    for (String header : headers) {
      String ip = request.getHeader(header);
      if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
        return ip;
      }
    }
    return request.getRemoteAddr();
}

3. 헤더의 기본 개념

X-Forwarded-For는 일반적으로 클라이언트의 원래 IP를 담기 위해 사용하는 가장 표준적인 헤더입니다.

  1. Proxy-Client-IP

    • 사용 목적: 일부 프록시 서버나 애플리케이션 서버에서 클라이언트의 원래 IP를 기록
    • 특징: 흔히 WebLogic과 같은 특정 서버 환경에서 사용되며, 모든 프록시가 이 헤더를 지원하지는 않습니다. 일반적인 환경보다는 특정 서버 설정에서 클라이언트의 IP를 추적하기 위해 추가됩니다.

  2. WL-Proxy-Client-IP

    • 사용 목적: Oracle WebLogic 서버에서 주로 사용되는 헤더로 프록시 서버를 통해 들어오는 요청의 원래 클라이언트 IP를 담습니다.
    • 특징: WebLogic 서버의 설정에 따라 자동으로 설정될 수 있으며 WebLogic 환경에서 클라이언트의 IP를 수집하는 데 사용됩니다. WebLogic을 사용하지 않는다면 이 헤더가 없는 경우가 많습니다.

  3. HTTP_CLIENT_IP

    • 사용 목적: 일부 프록시 서버 또는 애플리케이션에서 클라이언트 IP를 지정하기 위해 사용합니다.
    • 특징: 공식적인 표준은 아니지만 일부 환경에서 클라이언트 IP를 담기 위해 사용됩니다. 많은 경우 X-Forwarded-For로 대체되는 경우가 많아 널리 사용되지는 않습니다.

  4. HTTP_X_FORWARDED_FOR

    • 사용 목적: X-Forwarded-For와 유사하게 프록시 서버를 거친 원래 클라이언트의 IP를 담기 위해 사용됩니다.
    • 특징: X-Forwarded-For와 거의 동일한 역할을 하지만 환경에 따라 HTTP_X_FORWARDED_FOR로 설정되는 경우가 있습니다. 다중 프록시를 거치는 경우 IP가 여러 개 포함될 수 있으므로 주의가 필요합니다.

  5. X-Real-IP

    • 사용 목적: 클라이언트의 실제 IP를 기록하기 위해 프록시 서버에서 추가하는 헤더입니다.
    • 특징: X-Forwarded-For와 다르게 여러 IP가 아닌 단일 IP를 담도록 설계되었습니다. 클라이언트가 직접 이 헤더를 추가할 수 있기 때문에 신뢰할 수 있는 프록시 환경에서만 안전하게 사용할 수 있습니다.

  6. REMOTE_ADDR

    • 사용 목적: 클라이언트와 직접 연결된 마지막 장치(예: 프록시 서버 또는 로드 밸런서)의 IP를 자동으로 기록합니다.
    • 특징: 서버에서 기본적으로 제공하는 IP 정보로 모든 프록시를 거친 후의 마지막 IP 주소를 담습니다. 클라이언트의 IP가 아닌 경우가 많으므로 프록시 서버를 거치지 않은 직접 연결에서만 유효한 클라이언트 IP를 제공합니다.

정리

이처럼 다양한 헤더가 존재하지만 대부분의 경우 환경과 설정에 따라 특정 헤더만 포함될 수 있습니다.

일반적인 프록시 서버 설정에서는 X-Forwarded-For가 가장 많이 사용되 WebLogic이나 Oracle Web 서버와 같은 특정 환경에서는 Proxy-Client-IP나 WL-Proxy-Client-IP를 함께 고려하는 것이 좋습니다.

4. 결론

이제 개선된 코드를 통해 다중 IP를 올바르게 처리하고 중복된 코드 없이 효율적으로 클라이언트의 IP를 가져올 수 있습니다.

X-Forwarded-For와 같은 헤더는 클라이언트의 IP를 추적할 수 있는 유용한 정보지만 신뢰할 수 있는 환경에서 적절히 사용해야 정확한 결과를 얻을 수 있습니다.

https://www.junglog.xyz/blogs/post/26

0개의 댓글