HttpServletRequest의 getRemoteAddr()
가장 기본적인 방법이다.
이 방법은 클라이언트와 서버가 직접 통신할 때 유용하다.
그러나 프록시 서버나 로드 밸런서를 사용하는 경우에는 프록시나 로드밸런서의 주소가 반환될 수 있다.
다양한 헤더를 통해 얻기
프록시 서버나 로드밸런서 등의 존재 때문에, 클라이언트의 진짜 IP를 얻기 위해서는 아래와 같은 코드가 쓰이기도 한다.
mport javax.servlet.http.HttpServletRequest;
public static String getClientIp(HttpServletRequest request) {
String clientIp = request.getHeader("X-Forwarded-For");
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("Proxy-Client-IP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("WL-Proxy-Client-IP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("X-Real-IP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("X-RealIP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("REMOTE_ADDR");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getRemoteAddr();
}
return clientIp;
}
그냥 X-Forwarded-For 헤더부터 여러 헤더를 하나씩 뒤지는 코드이다.
다 알아야하는 헤더 값인가..? 싶었지만 X-Forwarded-For 헤더 말고는 정보도 잘 안나오고 다른 헤더들도 X-Forwarded-For와 동일한 역할을 하는 것 같았다.
그래서 X-Forwarded-For 헤더에 대해서만 간단히 정리해보고자 한다.
X-Forwarded-For
X-Forwarded-For(XFF) 요청 헤더는 프록시 서버를 통해 웹 서버에 연결하는 클라이언트의 원래 IP 주소를 식별하기 위한 사실상의 표준 헤더이다.
이 헤더의 표준화된 버전은 HTTP Forwarded 헤더이지만, 사용 빈도가 훨씬 낮다.
형식
X-Forwarded-For: <client>, <proxy>
X-Forwarded-For: <client>, <proxy>, …, <proxyN>
예시
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, 2001:db8:85a3:8d3:1319:8a2e:370:7348
클라이언트의 IP를 의미한다.
프록시의 IP 주소를 의미한다.
요청이 여러개의 프록시를 거친다면, 각각의 프록시가 리스트로 이어진다.
즉, 가장 오른쪽에 있는 아이피 주소가 가장 최근에 거친 프록시의 주소,
가장 왼쪽에 있는 IP 주소가 원래 클라이언트의 아이피가 된다.
설명
클라이언트가 서버에 연결하면, 클라이언트의 IP는 서버에 보내지고 대부분의 경우 이는 로그로 남겨진다.
만약 클라이언트의 연결이 어떤 forward나 리버스 프록시를 거치게되면, 서버는 마지막 프록시의 IP 주소만을 볼 수 있다. (이건 별로 쓸모가 없음)
특히 마지막에 거치는 프록시가 우리 시스템의 로드밸런서 같은거라면 더더욱 쓰잘때기 없음
서버에게 유용한 정보를 더 많이 주기위해 X-Forwarded-For 헤더가 쓰이는 것이다.
동작 방식
사진 출처: https://www.keycdn.com/support/x-forwarded-for
클라이언트로부터 요청을 받은 프록시 서버나 로드밸런서는 X-Forwarded-For 헤더를 추가하고 클라이언트의 원래 IP 주소를 추가할 수 있다.
AWS의 ELB나 CloudFront는 기본적으로 HTTP 요청 헤더에 X-Forwarded-For가 추가되어 있다.
이를 통해 백엔드 서버는 클라이언트의 원래 IP를 알 수 있다.
주의 사항
X-Forwarded-For 헤더는 클라이언트가 직접 설정 or 조작할 수 있다.
따라서 보안에 민감한 시스템에서는 이를 신뢰하면 안된다.
서버는 반드시 IP 정보를 검증하거나, 신뢰할 수 있는 프록시에서만 헤더를 추가할 수 있도록 제한해야한다.
또한, X-Forwarded-For의 클라이언트 IP는 100% 신뢰할 수 없기에, 이 값을 기반으로 특정 클라이언트를 차단하는 로직을 짠다면, 무고한 사용자의 IP를 차단하는 잘못된 결과를 낳을 수가 있다.
Reference
'개념 공부 > Network, Infra, CICD' 카테고리의 다른 글
[AWS] RDS 스냅샷(수동 스냅샷 생성, 자동 스냅샷 활성화, 스냅샷을 통한 복구 방법, 스냅샷 요금) (0) | 2025.04.22 |
---|---|
[Network] OSI 7계층 (0) | 2024.12.22 |
[AWS] Elastic IP(탄력적 주소)란? (0) | 2024.12.10 |
[CI/CD] Jenkins란 무엇인가 (0) | 2024.05.04 |