본 글은 야간모드에 최적화 되어있습니다. 우측 상단에서 해 혹은 달모양을 클릭시어 velog 설정을 야간모드로 해주시면 더욱 편안하게 읽으실 수 있습니다.
처음에는 PC 웹 위주로 서비스를 운영했지만, 점차 서비스가 확장되고 글로벌 서비스를 진행하는 회사이다보니 접속자의 IP에 대한 국가 판별과 언어, 그리고 접속 디바이스에 대한 구분이 필요해졌다.
그래서 어떤 방식으로 가져올 수 있을까 생각을 하다가 접속한 브라우저에서 정보를 얻어올 수 있지 않을까 싶었다.
브라우저에서 접속할 때 어떤방식으로든 api request가 일어날 테니, 그 request의 header 안에 있는 정보를 활용해 보자는 생각이 들었다.
그 첫번째로, IP 파악에 대해서 작성해본다.
찾아보니 request 안에서 X_Forward_For와 Remote_ADDR 이라는것을 활용하여 클라이언트의 IP를 얻을 수 있다고 한다.
방법은 간단했지만 방식과 원리도 궁금해서 조금 더 찾아보고 함께 작성해본다.
XFF는 HTTP Header 중 하나로 HTTP Server 에 요청한 clinet 의 IP 를 식별하기 위한 것이라고 한다.
우리가 어떠한 클라이언트의 IP 주소를 Request 안에서 얻으려고할 때 두가지가 존재하게 되는데, 그것이 바로 XFF와 RemoteADDR이다.
원래 실제 IP는 RemoteADDR에 가지고 있게되는데, Proxy 서버를 거치게되면 해당 값으로는 정확한 클라이언트의 IP를 알수없다.
그래서 존재하는것이 XFF인데, 이 값은 우리가 얻으려는 실제 IP로, 엄연하게 말하면 이 XFF는 Proxy 서버가 설정해주는 환경 변수이다.
클라이언트가 Proxy 서버를 통하여 요청을 보낼 경우 서버로 전송 되는 클라이언트 IP 정보인 Remote_ADDR은 더 이상 실제 IP가 아니게되고 Proxy 서버 IP 정보로 설정되어 전송되게 된다.
Via: 1.1 proxy.smileserv.com:80 (squid)
X-Forwarded-For: 220.90.215.4 # Proxy를 거쳐 request 요청을 하게된 IP
Cache-Control: max-age=259200
Connection: keep-alive
즉, Proxy를 거치게 되면 Proxy 서버가 설정해준 환경변수인 X_FROWARDED_FOR에 위 헤더 처럼 실제 IP값과 Proxy ip가 실려 전송을 요청하게되어 해당 부분의 맨 첫번째 IP가 실제 요청 IP가 된다.
그럼 왜 바꿔주는지도 궁금해져서 더 찾아보았다.
HTTP_X_FORWARDED_FOR를 사용하는 이유
웹 서버나 WAS 앞에 L4 같은 로드밸런서나 Proxy 서버, 캐싱 서버 등이 존재하는 경우에 웹서버는 Proxy 서버나 장비 IP 에서 접속한 것으로 인식하게 된다고 한다.
그렇기 때문에 웹서버는 실제 클라이언트 IP 가 아닌 앞단에 있는 Proxy 서버 IP 를 요청한 IP 로 인식하고, Proxy 장비 IP 로 웹로그를 남기게 된다.
클라이언트 IP ⟶ Proxy 서버 및 장비 ⟶ 웹 서버
이때 웹 프로그램에서는 Request Header 내에 있는 XFF를 통해 클라이언트 IP를 찾아 실제 요청한 클라이언트 IP 를 알 수 있고, 웹로그에도 실제 요청한 클라이언트 IP 를 남길 수 있게된다.
X-Forwarded-For: client, proxy1, proxy2
위와 같이 X-Forwarded-For 는 다음과 같이 콤마를 구분자로 Client 와 Proxy IP 가 들어가게 되므로 첫번째 IP 를 가져오면 클라이언트의 실제 IP가 어떤 것인지 알 수 있다.
추가적으로 Apache 나 Nginx에서 XFF를 사용하려면 추가적인 설정이 필요하다고 하니 필요하신분들은 검색해보시기를 바란다.
위에서 설명한 이유와 같이, 우리는 Proxy를 거친다하더라도 변조되지 않은 클라이언트의 실제 IP가 필요하다. Django에서도 Request 모듈을 통해서 이 기능을 지원하고 있다.
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
다른곳에서도 사용하는 방식은 비슷하더라.
짧지만 굉장히 유용하게 사용할 수 있는 코드인것 같고 header내에 수많은 값들 중에서 다른 부분들도 활용할 수 있다면 더 많은 정보를 활용할 수 있지 않을까 싶었다.
예를 들자면, 네이버나 이런곳에서도 사용하고있는 현재 접속위치와 다른위치에서 접속했을때 alert 페이지나 , 동시접속 기기에 대한 정보들 같은 것들?