티스토리 뷰
특정 API에 대해서 호출 가능한 IP를 제한하고 싶을 때 Spring Security의 hasIpAddress를 사용하면 제한 할 수 있다.
등록할 수 있는 방법은 2가지가 있다고 나온다
- @PreAuthorize 어노테이션 사용
- 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 에러코드가 발생한다! (호출이 잘 될수도 있다. 그래도 안되는 경우에 대해서 이유를 알고싶으면 마저 읽어보자)
무슨일인지 코드를 분석해보자.
SecurityFilterChain에 hasIpAddress를 등록해놓으면 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가 아닌 다른 방법을 사용하자)
'Spring' 카테고리의 다른 글
데이터의 모든 변경이력 관리하기 with Hibernate (1) | 2023.12.23 |
---|---|
윈도우 함수 사용하기 - (1/3) (0) | 2023.12.22 |
트랜잭션의 전파속성 중 Nested 사용해보기 (0) | 2023.12.02 |
DispatcherServlet (1) | 2023.11.18 |
- Total
- Today
- Yesterday