ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • IP White List: 보안 취약점과 강화 방안
    글또 2024. 6. 19. 01:32

     

    이전 글

     

    spring security white list 어노테이션 만들기

    안녕하세요. 이번에는 spring security의 PreAuthorize를 커스텀한 이야기를 해보려 합니다. 목표는 인증인가 없이도 white list에 등록된 ip라면 자원에 접근이 가능하도록 하는 것입니다. 먼저 이게 필요

    gisungcu.tistory.com

     
     

    0. 들어가며

    안녕하세요.
    이번에는 이전에 만들었던 IP white list 어노테이션을 보강하는 글을 써보고자 합니다.
    IP white list 기능을 도입하기 전에 IP 변조가 마음에 걸렸고 이 부분을 해결하고자 했습니다.
     
    글의 순서는 다음과 같습니다.
     
    1. spring에서 forward-headers-strategy 설정
    2. IP white list에 사용하는 IP가 어디서 오는지를 확인 (WAS별)
    3. 프록시 서버가 앞에 있을 경우의 IP가 변경되는 이유
    4. IP 인증/인가 대신 사용할 방법 소개
     
     

    1. server.forward-headers-strategy

     

     
    애플리케이션 앞에 로드밸런서나 프록시 서버가 있는 경우, 서버는 요청을 포워딩한 것을 모르고 로드밸런서와 프록시 서버를 클라이언트 취급합니다.
    클라이언트의 IP가 로드밸런서나 프록시 서버가 되는 것 입니다.
     
    x-forwarded-for 헤더를 사용하면 클라이언트 IP를 유추할 수 있습니다.
    이 유추하는 귀찮은 작업을 대신 위임할 수 있는데 이걸 WAS에게 맞길지 프레임워크에게 맞길지, 아니면 헤더를 사용하지 않을지를 정하는 spring설정입니다.
     
    구성은 NATIVE와 FRAMEWORK, NONE으로 나눠져 있습니다.
    해당 설정을 하지 않았다면 WAS에 따라 다르겠지만 나중에 IP를 얻을 때 socket의 IP를 얻게 될 것입니다.
     

    설정 간단 설명

    Native: WAS에게 위임합니다. 
    FRAMEWORK: 프레임워크에게 위임하는 것인데 spring ForwardedHeaderFilter를 동작시킵니다.
     
     
    Native설정을 했을 경우 WAS가 어떻게 클라이언트의 IP를 가져오는지 확인해 보도록 하겠습니다.
     

    2. WAS에 따른 클라이언트의 ip를 얻는 방식

    undertow VS tomcat 

     

    whitelistVoter

    IP white list는 서블릿에서 remoteAddr을 통해 IP를 꺼내 옵니다. 
    그렇다면 해당 서블릿의 IP는 어디서 올까요?
     
     

    tomcat:10.1.18

     

    RemoteIpValve

    IP를 얻기 위해 디버그를 찍고 타고 타고 가다 보면 tomcat의 RemoteIpValve에 도달하게 됩니다. 
    RemoteIpValve내부에서는 x-forwarded-for헤더의 값을 가져와서 검사를 합니다.
    끝에서부터 internalProxies이나 trustProxies에 걸리는 것들이면 다음 루프로 넘어갑니다.
    그러다 외부 IP를 발견하면 break로 빠져나와서 그 값을 서블릿에 세팅합니다.
    이렇게 클라이언트의 IP를 유추해서 넣는 것입니다.
     
     

    만약에 x-forwarded-for가 없다면 originalRemoteAddr을 사용하는데 이것은 socket에서 빼오는 IP입니다.
     

    RemoteIpValve는 무엇이냐면

    tomcat에서 valve라는 개념은 요청의 전처리를 하는 인터셉터입니다.
    그중 RemoteIpValve는 x-forwarded관련 헤더의 처리를 합니다.
     

    TomcatWebServerFactoryCustomizer

    이 valve는 어디서 세팅이 되냐면 tomcat이 처음 생성될 때 TomcatWebServerFactoryCustomizer 가 세팅합니다.
    앞에서 설정했던 server.forward-headers-strategy 값을 보고 넣는 것입니다.
    server.forward-headers-strategy 설정이 없는 경우는 RemoteIpValve를 타지 않습니다.
    그러면 spring에서 서블릿의 remoteAddr를 얻을 때 socket의 ip를 사용합니다.
     
    코드를 보면 클라우드 플랫폼이면 그냥 RemoteIpValve를 세팅합니다. 쿠버네티스나 헤로쿠 위에서 돌리면 모르는 세팅이 그냥 들어가는 것입니다.
     
     

    Undertow :2.3.7

    회사에서는 undertow를 사용하고 있기 때문에 해당 WAS도 어떻게 IP를 가져오는지 확인해 보도록 하겠습니다.
     

    ProxyPeerAddressHandler
    ProxyPeerAddressHandler

     
    tomcat에는 valve가 있었다면 undertow에는 handler가 있습니다. 
    forwardHeader의 전처리는 바로 ProxyPeerAddressHandler가 하게 됩니다.
    ProxyPeerAddressHandler에서는 x-forwarded-for의 값이 있는지 확인하고 있다면 가장 첫 번째 것을, 없다면 세팅하지 않습니다.
     
     

    UndertowWebServerFactoryCustomizer

    UndertowWebServerFactoryCustomizer가 ProxyPeerAddressHandler를 세팅합니다.
    구조를 보다 보니 tomcat과 많이 비슷합니다.
     

    undertow VS tomcat 

    결국 x-forwarded-for를 클라이언트에서 조작하면 IP White list가 통과하게됩니다.

     

    3. x-forwarded-for는 프록시와 로드밸런서에서 어떻게 동작하는지를 살펴봅니다.

     
    왜 프록시, 로드밸런서가 앞에 있으면 서버는 클라이언트의 IP를 알 수 없는 것일까요?
     
     
     
     

    https://aws.amazon.com/jp/builders-flash/202204/way-to-draw-architecture/?awsf.filter-name=*all

     
    1. 클라이언트의 요청을 게이트웨이를 통해 로드밸런서(L7)가 받게 됩니다.
    2. DMZ 내의 로드밸런서는 들어온 요청을 받고 정의된 목적지의 IP 주소를 얻습니다. (private DNS를 사용하는 경우도 있음)
    3. 로드밸런서는 IP주소가 해당 서브넷의 routing table에 있는지 확인합니다.
    4. 있다면 TCP 세션을 맺습니다. (스위치에는 ARP테이블이 있기에 IP를 통해 MAC주소를 알 수 있음)
    5. 없다면 게이트웨이를 통해 밖으로 나가 IP와 Port로 통신을 합니다.
     
     
    TCP 세션을 로드밸런서가 맺기 때문에 WAS에서는 소켓의 IP가 로드밸런서나 Proxy의 IP가 되는 것입니다.
    로드밸런서나 proxy에 x-forward-headers관련 설정을 추가해야 헤더에 client Ip를 콤마로 추가해줍니다.

     

    4. 대안책을 살펴봅니다

    우리의 애플리케이션은 x-forwarded-for를 사용해서 IP를 가져오고 관리하겠다고 선언하고 있습니다.
    외부 노출이 되지 않는 internal API이면 몰라도 IP만으로 인증/인가를 통과시키는 것은 위험합니다.
    x-forwarded-for는 위조가 가능하기 때문이죠.
     
     

    https://cloud.google.com/endpoints/docs/openapi/when-why-api-key?hl=ko

     
    그러다 생각해 낸 방식은 open API들이 많이 사용하는 API access key 방식입니다.
    서버와 약속된 Key를 가지고 요청을 받는 다면 해당 요청을 허용해 주는 방식입니다.
     
    장점으로는 구현의 편의와 보안이 더 향상되었습니다.
    단점으로는 Key유출, Key관리등이 있습니다.
    Key유출이 걱정된다면 매일 변하는 날짜데이터 혹은 동적인 Key를 저장해서 같이 해쉬 암호화를 한다면 더 안전할 것입니다.
     

    HttpAuthTokenWebAuthorizationManager
    HttpAuthTokenValidator

     
     
     

    5. 마치며

    IP White list를 만들면서 고민했던 부분을 고칠 수 있어서 좋았습니다.
    원점부터 다시 공부하면서 WAS의 코드가 어떻게 구현되었는지, 네트워크가 어떻게 구성되었는지를 볼 수 있었습니다. 
     
    비즈니스 관련 로직만 짜다가 네트워크에 대해서 다시 공부할 수 있는 좋은 기회였습니다.
    로드밸런서가 요청을 전달하는 방법은 여기에 소개한 방법 말고도 더 많은 방법이 있으니 찾아보시길 바랍니다. 

     
     

    Sample Code

     

    GitHub - ChoiGiSung/spring-security-white-list: spring-security-white-list

    spring-security-white-list. Contribute to ChoiGiSung/spring-security-white-list development by creating an account on GitHub.

    github.com

     
     


    + aspectj 사용 시 security용 meta annotation이 동작하지 않을 수 있습니다.

     

    댓글

Designed by Tistory.