티스토리 뷰

Spring

[Security] hasIpAddress 사용하기

개발하고싶은개발자 2024. 3. 9. 17:09

특정 API에 대해서 호출 가능한 IP를 제한하고 싶을 때 Spring SecurityhasIpAddress를 사용하면 제한 할 수 있다.

 

등록할 수 있는 방법은 2가지가 있다고 나온다

  1. @PreAuthorize 어노테이션 사용
  2. SecurityFilterChain 에 등록

 

그러나 현재 Spring Security 5.7.6 을 사용중인데 1번 방법인 @PreAuthorize에는 존재하지 않아서 2번째 방법인 SecurityFilterChain을 생성하면서 해당 설정을 등록해봤다.

 

 

/member 하위 API에 대해 만약 특정 IP(192.168.0.32)의 요청만 허용한다가 가정하면 아래와 같이 작성할 수 있다.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/member/**").hasIpAddress("192.168.0.32")
        .anyRequest().permitAll();

    return http.build();
}

 

그럼 실행해보자.

현재 내 ip가 192.168.0.32가 아니라면 위의 요청에 대해서 403 에러가 발생해야 한다. 하지만 정상적으로 호출이 된다!

 

 

왜 안될까?

찾아보니 보안상의 이유로 hasIpAddress를 위와같이 직접 호출하면 작동하지 않고 access를 통해서 등록을 해주어야 한다고 한다. (https://www.inflearn.com/questions/628277/hasipaddress-quot-제-아이피-quot-허용안됩니다)

 

 

그럼 access를 통해 등록되도록 수정해보자.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/member/**").access("hasIpAddress('" + "192.168.0.32" + "')")
        .anyRequest().permitAll();

    return http.build();
}

 

 

다시 실행해보자.

이제 정상적으로 호출이 안되고 403 에러코드가 발생하는 것을 볼 수 있다.

그러면 이제 다시 localhost(127.0.01)를 등록하고 호출이 되는지 확인해보자.

 

이제 다시 실행해보면 IP가 같은데도 호출이 안되고 403 에러코드가 발생한다! (호출이 잘 될수도 있다. 그래도 안되는 경우에 대해서 이유를 알고싶으면 마저 읽어보자)

 

 

 

무슨일인지 코드를 분석해보자.

SecurityFilterChainhasIpAddress를 등록해놓으면 API가 호출될 때 security를 통해서 검증을 하게 되는데 이 때 호출되는 함수가 WebSecurityExpressionRoot class의 hasIpAddress 메소드이다. 이 클래스 내부에서 IpAddressMatcher를 사용하여 ip 검증을 하게되는데(matches 메소드) 이 때 검증되는 코드를 보면은 hasIpAddress에 등록해놓은 IP의 주소체계와 호출한 IP의 주소체계가 다르면은 false를 리턴해버린다. 우리가 호출한 IP의 주소체계가 IPv6이고 등록한 IP는 IPv4(127.0.01)이기 때문에 호출이 안되는 것이었다!

// WebSecurityExpressionRoot의 matches 메소드
public boolean matches(String address) {
    InetAddress remoteAddress = parseAddress(address);
    //여기에서 false를 리턴해버린다!
    if (!this.requiredAddress.getClass().equals(remoteAddress.getClass())) {
        return false;
    }
    if (this.nMaskBits < 0) {
        return remoteAddress.equals(this.requiredAddress);
    }
    byte[] remAddr = remoteAddress.getAddress();
    byte[] reqAddr = this.requiredAddress.getAddress();
    int nMaskFullBytes = this.nMaskBits / 8;
    byte finalByte = (byte) (0xFF00 >> (this.nMaskBits & 0x07));
    for (int i = 0; i < nMaskFullBytes; i++) {
        if (remAddr[i] != reqAddr[i]) {
            return false;
        }
    }
    if (finalByte != 0) {
        return (remAddr[nMaskFullBytes] & finalByte) == (reqAddr[nMaskFullBytes] & finalByte);
    }
    return true;
}

 

이제 이유를 알았으니 IP를 IPv6로 등록하면 정상적으로 호출이 잘 된다!

 

이외에도 다른 방법으로는 자바에서 IPv6가 아닌 IPv4로 호출한 IP를 얻어오는 방법인데, 이 방법은 Java를 실행할 때 -Djava.net.preferIPv4Stack=true 옵션을 주면된다. 그러면 IPv6가 아닌 IPv4로 얻어와서 정상적으로 실행된다.

(아니면 Spring Security가 아닌 다른 방법을 사용하자)

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30