1. 계기

이번에 새로운 프로젝트를 시작하게 되었는데 로그인에 대한 구현이 필요해서 다음과 같이 Spring Security를 사용해서 JWT 방식으로 로그인을 구현해보려고한다.


2. JWT란?

JWT는 JSON Web Token으로 Header, Payload, Signature등으로 나눈 정보를 Base64 URL-safe Encode을 통해서 인코딩해 직렬화 한 방식이다.
사실, 다른 블로그에서 설명을 잘해놨기 때문에 이 정도만 알고 있으면 좋을것 같다.
참고자료


3. 인증방식

인증 방식은 우선 AccessTokenRefreshToken을 사용해서 구현할 예정이다. 그래서 다음 형식을 지닌다.
사실 이 부분은 다른 블로그에도 잘 설명이 되어있기 때문에 참고링크를 통해서 확인해보면 좋을것 같다.

1. 계기

이번에 새로운 프로젝트를 시작하게 되었는데 로그인에 대한 구현이 필요해서 다음과 같이 Spring Security를 사용해서 JWT 방식으로 로그인을 구현해보려고한다.


2. JWT란?

JWT는 JSON Web Token으로 Header, Payload, Signature등으로 나눈 정보를 Base64 URL-safe Encode을 통해서 인코딩해 직렬화 한 방식이다.
사실, 다른 블로그에서 설명을 잘해놨기 때문에 이 정도만 알고 있으면 좋을것 같다.
참고자료


3. 인증방식

인증 방식은 우선 AccessTokenRefreshToken을 사용해서 구현할 예정이다. 그래서 다음 형식을 지닌다.
사실 이 부분은 다른 블로그에도 잘 설명이 되어있기 때문에 참고링크를 통해서 확인해보면 좋을것 같다.


다음과 같은 로직으로 작동하게 만드려고한다. 여기서 세부적으로 설명해야할 부분들을 설명하도록 하겠다.

3-1. Refresh Token의 사용이유

이번 프로젝트에서 고민했던 것이 Refresh 토큰을 사용하는 부분에서 고민이 많았다.

여러가지 자료들을 참고했지만, 살짝씩 다른 말들뿐이었다. 그래서 소신대로 해보자라고 생각하고 다음처럼 구성을 했다.

우선 설명하기 전에 Refresh Token왜 생겨났냐?에 대한 이야기를 해보려고한다. 우선 각 토큰에는 만료시간이라는 것이 존재해, 이 만료시간이 끝난다면, 토큰은 더 이상 사용하지 못한다는 점이 존재한다.

그래서, 보안을 위해서 보통 Access Token의 만료시간을 짧게 주고 자주 재발급 받는 형식을 사용을한다. 하지만 여기에서 문제점이 생긴다. Access Token만 존재한다면 재발급 받는 방법은, 다시 로그인을 하는 방법 뿐이다.

사용자들은 자주 로그인 하는것을 싫어하기 때문에 UX적인 측면을 고려하지 못할 수도 있다. 그렇다고, 만료시간을 길게 잡는다면 만료시간이 길어져서 공격자가 토큰을 탈취하게 된다면, 공격자인지 사용자인지 서버는 모르게되고 사용자의 정보는 탈취당하게 된다.

그래서 Refresh Token이 등장하게 되었다. Refresh Token과 Access Token을 같이 로그인을 할때 발급을 하는 방식을 사용하려고 한다.

이런 Refresh Token을 관리하는 방식도 여러가지가 있다 크게는 다음과 같이 운영하는것 같다.

  1. Local Storage에 프론트가 저장을 한다.
  2. Cookie를 통해서 Refresh Token을 저장한다 (HttpOnly 속성을 넣어서)
  3. 서버가 Refresh Token을 관리한다.

결론적으로

  • Access Token의 만료시간이 길면, 탈취당하고 공격자에 의해 이용당할 수 있음
  • Access Token의 만료시간이 짧으면, UX 측면에서 사용자는 짧은 시간주기로 계속해서 재로그인을 해야함

3-2 Front에서 Local storage에 저장해서 사용하는 방식

XSS 공격등으로, 탈취당할 문제점이 존재합니다. 하지만, 제일 편한 방식입니다.
또한, LocalStorage는 자바스크립트로도 접근이 가능합니다.

window.localStorage.getItem();

3-3 Front에서 Cookie

HttpOnlySecure이나 이런 정보를 넣어 LocalStorage 보다는 안전하게 저장을 할 수 있습니다.
하지만, 이것도 CSRF공격등으로 탈취당할 수 있습니다.

3-4 Server에서 저장하는 방식

Session 또는 DB에 저장할 수 있습니다. 하지만, 이 방식은 Server에 무리를 일으킨다는 단점이 존재합니다. 그래서 현재는 Redis같은 In-Memory DB 방식을 사용해서 사용을 합니다.

4. 프로젝트에 결정한 방식

프로젝트에서는 간단하게 Cookie방식을 통한 방법을 고려하기로 했습니다.
당연히 Security는 안전하게 할 수록 좋지만, 어떤 방식이던지 취약점은 존재하기 때문입니다.
100% 막을 수 있는 보안방법은 없습니다. 또한, 각 방식마다 장단점이 존재하기 때문에 결론적으로는 CookieHttpOnlySecure Flag를 추가해서 배포하기로 마음먹었습니다.


5. 아니 그래서 HttpOnly랑 Secure가 뭔데?

계속해서 HttpOnlySecure를 넣어서 안전하게 사용할것이다라고 하는데 그래서 이게 뭔지에 대한 설명이 적은것 같다.

우선, HttpOnly는 자바스크립트로 쿠키를 조회하는 것을 막는 옵션이라고 보면 좋다.
그리고, Secure는 HTTPS로 통신하는 경우에만 쿠키를 서버로 전송하는 옵션이라고 보면 좋다.

자세한 사항이 궁금하면 해당 링크를 참고하면 좋을것 같다.

6. Refresh Token에 대해서 검증하고 동작하는 로직 결정

내가 가장 고민했던 방식에 대해서 설명하려고 한다.

다른 블로그나 참고자료에서는 이 부분에 대해서 설명한 곳이 별로 없거니와, 사람마다 차이점이 존재하기 때문에 그래서 소신대로 라는 말을 붙여서 제목에 의미를 더했다.

검증 방법은 다음과 같은 종류를 생각했다.

첫 번째로 Access Token에 문제가 있는 경우는 다음과 같은 경우가 있다고 판단하였습니다.

  1. Authorized 헤더에 Access Token 값이 없거나 헤더에 대한 정보가 없는 경우
  2. Access Token이 유효하지 않은 토큰인 경우
  3. Access Token의 시간이 만료된 경우

마지막으로는 Refresh Token에 대해서 문제가 있는 상황은 다음과 같은 경우를 생각했습니다.

  1. Access Token이 만료되지 않았을 때 Refresh Token을 사용해 재발급 받는 경우는 해당 공격자가 탈취를 한경우라고 판단하여 두 토큰을 모두 만료시키는 방식으로 하였습니다.
  2. Refresh Token이 Cookie에 존재하지 않는경우
  3. Refresh Token으로 DB에 유저정보를 검색하는데, 이 토큰을 가지고 있는 사용자가 없는 경우
  4. Refresh Token의 유효시간이 만료된 경우
  5. Refresh Token이 유효하지 않은 토큰인 경우

다음과 같은 상황을 검증하려고 합니다.


7. 마지막으로

이제 다음 부터는 직접 Spring Security를 써서 어떻게 구축을 했는지를 확인하려고 합니다.
다음과 같은 로직으로 작동하게 만드려고한다. 여기서 세부적으로 설명해야할 부분들을 설명하도록 하겠다.

3-1. Refresh Token의 사용이유

이번 프로젝트에서 고민했던 것이 Refresh 토큰을 사용하는 부분에서 고민이 많았다.

여러가지 자료들을 참고했지만, 살짝씩 다른 말들뿐이었다. 그래서 소신대로 해보자라고 생각하고 다음처럼 구성을 했다.

우선 설명하기 전에 Refresh Token왜 생겨났냐?에 대한 이야기를 해보려고한다. 우선 각 토큰에는 만료시간이라는 것이 존재해, 이 만료시간이 끝난다면, 토큰은 더 이상 사용하지 못한다는 점이 존재한다.

그래서, 보안을 위해서 보통 Access Token의 만료시간을 짧게 주고 자주 재발급 받는 형식을 사용을한다. 하지만 여기에서 문제점이 생긴다. Access Token만 존재한다면 재발급 받는 방법은, 다시 로그인을 하는 방법 뿐이다.

사용자들은 자주 로그인 하는것을 싫어하기 때문에 UX적인 측면을 고려하지 못할 수도 있다. 그렇다고, 만료시간을 길게 잡는다면 만료시간이 길어져서 공격자가 토큰을 탈취하게 된다면, 공격자인지 사용자인지 서버는 모르게되고 사용자의 정보는 탈취당하게 된다.

그래서 Refresh Token이 등장하게 되었다. Refresh Token과 Access Token을 같이 로그인을 할때 발급을 하는 방식을 사용하려고 한다.

이런 Refresh Token을 관리하는 방식도 여러가지가 있다 크게는 다음과 같이 운영하는것 같다.

  1. Local Storage에 프론트가 저장을 한다.
  2. Cookie를 통해서 Refresh Token을 저장한다 (HttpOnly 속성을 넣어서)
  3. 서버가 Refresh Token을 관리한다.

결론적으로

  • Access Token의 만료시간이 길면, 탈취당하고 공격자에 의해 이용당할 수 있음
  • Access Token의 만료시간이 짧으면, UX 측면에서 사용자는 짧은 시간주기로 계속해서 재로그인을 해야함

3-2 Front에서 Local storage에 저장해서 사용하는 방식

XSS 공격등으로, 탈취당할 문제점이 존재합니다. 하지만, 제일 편한 방식입니다.
또한, LocalStorage는 자바스크립트로도 접근이 가능합니다.

window.localStorage.getItem();

3-3 Front에서 Cookie

HttpOnlySecure이나 이런 정보를 넣어 LocalStorage 보다는 안전하게 저장을 할 수 있습니다.
하지만, 이것도 CSRF공격등으로 탈취당할 수 있습니다.

3-4 Server에서 저장하는 방식

Session 또는 DB에 저장할 수 있습니다. 하지만, 이 방식은 Server에 무리를 일으킨다는 단점이 존재합니다. 그래서 현재는 Redis같은 In-Memory DB 방식을 사용해서 사용을 합니다.

4. 프로젝트에 결정한 방식

프로젝트에서는 간단하게 Cookie방식을 통한 방법을 고려하기로 했습니다.
당연히 Security는 안전하게 할 수록 좋지만, 어떤 방식이던지 취약점은 존재하기 때문입니다.
100% 막을 수 있는 보안방법은 없습니다. 또한, 각 방식마다 장단점이 존재하기 때문에 결론적으로는 CookieHttpOnlySecure Flag를 추가해서 배포하기로 마음먹었습니다.


5. 아니 그래서 HttpOnly랑 Secure가 뭔데?

계속해서 HttpOnlySecure를 넣어서 안전하게 사용할것이다라고 하는데 그래서 이게 뭔지에 대한 설명이 적은것 같다.

우선, HttpOnly는 자바스크립트로 쿠키를 조회하는 것을 막는 옵션이라고 보면 좋다.
그리고, Secure는 HTTPS로 통신하는 경우에만 쿠키를 서버로 전송하는 옵션이라고 보면 좋다.

자세한 사항이 궁금하면 해당 링크를 참고하면 좋을것 같다.

6. Refresh Token에 대해서 검증하고 동작하는 로직 결정

내가 가장 고민했던 방식에 대해서 설명하려고 한다.

다른 블로그나 참고자료에서는 이 부분에 대해서 설명한 곳이 별로 없거니와, 사람마다 차이점이 존재하기 때문에 그래서 소신대로 라는 말을 붙여서 제목에 의미를 더했다.

검증 방법은 다음과 같은 종류를 생각했다.

첫 번째로 Access Token에 문제가 있는 경우는 다음과 같은 경우가 있다고 판단하였습니다.

  1. Authorized 헤더에 Access Token 값이 없거나 헤더에 대한 정보가 없는 경우
  2. Access Token이 유효하지 않은 토큰인 경우
  3. Access Token의 시간이 만료된 경우

마지막으로는 Refresh Token에 대해서 문제가 있는 상황은 다음과 같은 경우를 생각했습니다.

  1. Access Token이 만료되지 않았을 때 Refresh Token을 사용해 재발급 받는 경우는 해당 공격자가 탈취를 한경우라고 판단하여 두 토큰을 모두 만료시키는 방식으로 하였습니다.
  2. Refresh Token이 Cookie에 존재하지 않는경우
  3. Refresh Token으로 DB에 유저정보를 검색하는데, 이 토큰을 가지고 있는 사용자가 없는 경우
  4. Refresh Token의 유효시간이 만료된 경우
  5. Refresh Token이 유효하지 않은 토큰인 경우

다음과 같은 상황을 검증하려고 합니다.


7. 마지막으로

이제 다음 부터는 직접 Spring Security를 써서 어떻게 구축을 했는지를 확인하려고 합니다.

'Spring > 스프링 시큐리티' 카테고리의 다른 글

스프링 시큐리티 인 액션 (2)  (1) 2023.01.31
스프링 시큐리티 인 액션 (1)  (0) 2023.01.31

+ Recent posts