요즘에 회사생활도 일상생활도 바빠져서 포스팅을 오랫동안 하지 못했는데,
나태해진 나를 반성하고 반성하며

오랜만에 포스팅을 하려고 한다.
포스팅 주제를 생각하다가 과거에 API 접근관련하여
Spring Security의 Autority를 사용한 적이 있는데,
최근 회사에서 새로운 서비스를 개발할 때 사용할 수 있을 것 같아서기억을 되새김질할 겸 정리를 해보려고 한다.
API 접근 권한은 왜 부여하는 걸까?
'API 보안이 중요하다'라는 말은 많이 들어봤을 것이다.
API에 접근 권한을 부여하는 것은 이 보안의 모범사례 중 하나일 뿐이다.
그렇다면 API 보안은 왜 중요할까?
기업은 API를 이용해서 서비스를 연결하고 데이터를 전송하는데
이런 API가 손상, 노출 또는 해킹이 되면 주요한 데이터의 보안 유출사고의 원인이 된다.
이로 인해서 민감한 의료, 금융 및 개인정보가 노출이 되는 것이다.
하지만 모든 데이터가 동일한 것은 아니므로 이를 보호하는 방식도 달라야 하며
API 보안 접근 방식은 전송되는 데이터의 종류에 따라서 달라지게 된다.
결국 우리가 함부로 노출되서는 안 될 중요한 데이터를 보호하기 위해선 꼭 API가 보안이 되어야 하는 것이다!
일반적인 API 보안 모범 사례는 다음과 같다고 한다.
1. 토큰을 사용한다.
신뢰할 수 있는 여러 Identity를 설정한 후
해당 Identity에 할당된 토큰을 사용하여 서비스 및 리소스에 대한 엑세스를 제어한다.
2. 암호화 및 서명을 사용한다.
TLS와 같은 방법을 사용하여 데이터를 암호화한다.
올바른 사용자 외에 다른 누구도 내 데이터의 암호를 해제하고 수정할 수 없도록 서명을 요구한다.
3. 취약점을 확인한다.
운영체제, 네트워크, 드라이버 및 API 구성요소를 최신 상태로 유지한다.
모든 구성 요소의 동작 방식을 파악하고 사용자 API 보안 침해에 사용될 수 있는 취약점을 확인한다.
4. 할당량 및 제한을 사용한다.
API 호출 빈도에 대한 할당량을 설정하고 사용 기록을 추천한다.
API 호출이 증가할수록 이것이 악용되고 있다는 의미일 수 있다.
API 호출 무한 반복과 같은 프로그래밍상의 실수일 수 도 있다.
Access Role을 만들어 급격한 트래픽 증가와 Dos 공격으로부터 API를 보호한다.
5. API 게이트웨이를 사용한다.
API 게이트웨이는 주요 API 트래픽 실행 지점 역할을 한다.
안전한 게이트웨이를 이용하여 트래픽을 인증하고 API 사용 방식을 제어 및 분석할 수 있다.
여기서 내가 하고자 하는 API 접근 권한은 4번 중에 일부분이 될 것이다!
그렇다면 이 API 접근 권한 관리를 위한 Spring Security Autority 사용법을 알아보자.
앞서 나는 Spring security 도입기 업무 회고에도 작성했다시피
로그인을 한 (Http Header에 jwt데이터가 있을 시) 계정에 대해서는
Spring Security Context Holder에 계정 정보가 세팅된 Authentication 객체를 담아주게 되는데,
해당 계정 정보(UserDetails)에는 권한에 대한 데이터를 추가할 수 있는 필드가 존재한다.
private static final long serialVersionUID = 520L;
private static final Log logger = LogFactory.getLog(User.class);
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
UserDetail를 implement 해서 사용 중인 Spring Sercurity User객체가 가지고 있는 필드이다.
이 중 authorities 데이터를 활용하여 권한에 관련된 Role을 세팅해주고 API Access에 사용할 수 있다.
Authentication 객체가 잘 설정이 되었으면 그 후처리는 모두 Spring Security가 알아서 해준다.
우리는 설정만 잘해주면 된다!
설정을 해주는 방식은 두 가지 방식이 있다.
1. Srping Security Config file 방식
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/about").authenticated()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().permitAll();
}
}
2. API Annotation 방식
@GetMapping(value = "/admin")
@PreAuthorize("hasRole('ADMIN')")
public User getUser(@PathVariable long id) {
return userService.getUser(id);
}
Annotaion 방식으로 사용할 수 있는데 위의 예제는 @PreAuthorize를 사용하였다.
'ADMIN'이라는 Role을 가지고 있는 사용자 객체가 접근을 한다면 허용해 준다는 의미이다.
@PreAuthorize
@PostAuthorize
@Secure
내부에 사용할 수 있는 함수로도 여러 가지를 제공한다.
위의 예제에서는 hasRole이라는 함수를 사용했고,
말 그대로 '해당 Role을 가지고 있냐'를 묻는 함수이다.
내가 활용한 방안은 다음과 같다.
아래 소스는 패스워드를 변경하는 API 컨트롤러의 일부이다.
@PreAuthorize(
"isAuthenticated() " +
"and (#passwordRequest.email == principal.getUsername() " +
"or hasAuthority(T(com.common.constants.AccessRole).ADMIN.name()))"
)
@RequestMapping(value = "changePassword", method = RequestMethod.POST, produces = "application/json")
public Object changePassword(@RequestBody PasswordRequest passwordRequest) {
return Response.responseBuilder().withType("auth").withData(authService.changePassword(passwordRequest)).build();
}
@PreAuthorize : 비즈니스 로직이 시작하기 전에 접근권한을 체크하겠다는 Annotation이다.
- isAuthenticated()
인증된 계정에 대해서만 허용한다.
우리 프로젝트에서 인증의 여부는 API 호출 시 통신용 토큰의 여부로 판단하였다.
- #passwordRequest.email == principal.getUsername()
Request Body에 담겨온 passworkRequest라는 객체 내에
email이라는 필드 값과 Spring security의 Principal에 담긴 user name의 값이 동일할 때만 허용한다.
비밀번호는 본인 꺼만 변경할 수 있어야 하기 때문이다.
- hasAuthority(T(com.common.constants.AccessRole).ADMIN.name())
AccessRole이라는 Enum에서 ADMIN이라는 AccessRole을 가진 계정의 호출이라면 허용한다.
관리자가 일반 사용자의 비밀번호를 변경할 수 있어야 하기 때문이다.
결국 저 기능을 통해서
비즈니스 로직을 수행하기 전, 인증된 계정인지 여부를 파악하고,
파라미터로 넘어온 내용과 실제 요청하는 계정의 정합성을 체크하거나
관리자 권한을 가진 계정인지 여부를 체크하여 모든 절차가 통과되면 실제 비지니스 로직을 통과하게 구현하였다!
오늘의 느낀 점
보안은 어렵다! 끝.
'Programing > Spring' 카테고리의 다른 글
Proxy Pattern 으로 구현해보는 다이나믹 서비스 적용기 (0) | 2022.08.31 |
---|---|
RestTemplate로 알아보는 Spring Template Callback Pattern (0) | 2022.07.17 |
WAS (Spring Boot) - DB 성능 개선과 최적화 (4) - RedisTemplate (0) | 2022.06.02 |
WAS (Spring Boot) - DB 성능 개선과 최적화 (3) - RestClient (0) | 2022.06.02 |
Spring 양방향 Dependency Injection (0) | 2022.05.29 |