Spring HttpRequestHeader 에서 사용자 IP를 찾아보자 client ip 찾기

태그
Spring
HTTP
request
header
client
공개여부
작성일자
2020/03/17

client의 IP주소를 알아내야 한다.

http_request_headers2.png
IP를 사용하여 누가 나를 호출했는지 알수 있다면, 로깅에서 편리할 수 있습니다. 사내에서 사용하는 API 들은 별도의 인증과정을 두지 않는 대신 network 로 호출, 차단을 관리합니다. 따라서, 인증이 없는것은 편리함이 따라오지만 누가 나를 불렀는지 애매해지는 경우가 많습니다.
elastic search 를 사용해 로그 모니터링을 하게 되면서, 어떤 서버가 무슨 API 를 호출을 언제 하였고, 결과가 어땟는지 확인하는 과정이 필요했습니다.
또한, 외부에선 호출이 불가능하더라도 우리 회사 IP라면 authorization 과정을 생략하는데도 IP를 사용합니다.
그러면 client 의 IP address 어디에 있을까요??

HttpRequestServlet

보통 controller 에서 HttpRequestServlet 을 매개변수로 받아오는 경우가 있습니다
public IdolData getList(@RequestParam String name, HttpRequestServlet request) { request.getRemoteAddr(); // IP 주소? }
Java
이렇게 해서 IP 주소를 받아온다면, 로컬에서는 IPv6 로 IP 주소를 반환합니다.
하지만, 서버에서는 127.0.0.1 을 받아오는 이슈가 있었습니다.
우리는 대부분 proxy 환경, docker 등에서 API 어플리케이션을 띄어두기 때문에 발생하는 이슈입니다.
이럴 때 사용하는것이 X-Forwared-For 입니다.
X-Forwared-For: <client>, <proxy 1>, <proxy 2>
Java
client 의 IP 주소 입니다.
바로 우리가 찾던 그것!!

<proxy 1>, <proxy 2>

하나의 요청이 proxy 들을 거치면서 IP 주소들이 차례로 열거됩니다.
가장 오른쪽 IP 주소가 가장 마지막에 거친 proxy IP 주소.
가장 왼쪽 IP 주소가 최초 proxy 주소
application 앞에 있는 proxy 의 IP 주소를 반환합니다.
X-Forwarded-For: 2001:db8:85a3:8d3:1319:8a2e:370:7348 X-Forwarded-For: 203.0.113.195 X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178
Java
그런데 이런 header 뿐만이 아니라
X-Forwarded-For
Proxy-Client-IP
WL-Proxy-Client-IP
HTTPXFORWARDED_FOR
HTTPXFORWARDED
HTTPXCLUSTERCLIENTIP
HTTPCLIENTIP
HTTPFORWARDEDFOR
HTTP_FORWARDED
HTTP_VIA
REMOTE_ADDR
이러한 header 들도 봐둬야 할 필요가 있습니다. (각 header 들에 대한 설명은 필요해지면 하는 걸로)
그래서 저는 다음과 같은 Util class를 만들어 보았어요
public class HttpReqRespUtils { private static final String[] IP_HEADER_CANDIDATES = { "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED", "HTTP_X_CLUSTER_CLIENT_IP", "HTTP_CLIENT_IP", "HTTP_FORWARDED_FOR", "HTTP_FORWARDED", "HTTP_VIA", "REMOTE_ADDR" }; public static String getClientIpAddressIfServletRequestExist() { if (Objects.isNull(RequestContextHolder.getRequestAttributes())) { return "0.0.0.0"; } HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); for (String header: IP_HEADER_CANDIDATES) { String ipFromHeader = request.getHeader(header); if (Objects.nonNull(ipFromHeader) && ipFromHeader.length() != 0 && !"unknown".equalsIgnoreCase(ipFromHeader)) { String ip = ipFromHeader.split(",")[0]; return ip; } } return request.getRemoteAddr(); } }
Java
생각해보니 RequestContextHolder 는 여러모로 도움을 많이 받네요
그 안에서 사용하는 것은
/** * Return the RequestAttributes currently bound to the thread. * @return the RequestAttributes currently bound to the thread, * or {@code null} if none bound */ @Nullable public static RequestAttributes getRequestAttributes() { RequestAttributes attributes = requestAttributesHolder.get(); if (attributes == null) { attributes = inheritableRequestAttributesHolder.get(); } return attributes; }
Java
이 함수를 사용해 RequestAttributes 라는 interface 를 가져옵니다.
ServletRequestAttributes 를 가져와 thread 에 bound 된 servlet request 의 데이터를 받아와
이 thread 로 요청을 보낸 client 의 정보를 수집합니다.
제 서버 상황에서는 X-Forwared-For 의 데이터를 반환받지만, 또 다른 환경이고 다른 상황이라면 다른 header 의 값을 가져와 확인하겠네요

결론

무조건 controller 에서 받아온 Request header 의 remote address 를 확인해선 안된다.
내 서버 상황에 맞는 (proxy 유무) 를 확인하여 필요한 header 의 값을 가져오자!!
Made with 💕 and Oopy