JWT와 OAuth2.0

hyelim·2023년 5월 5일
3
post-thumbnail

📌 JWT

세션의 단점

쿠키에 사용자 정보를 담아 인증을 거치는 것 보다 안전하지만, 해커가 쿠키를 탈취한 후 그 쿠키를 이용해 HTTP 요청을 보내면 서버는 사용자로 오인해 정보를 전달하게 된다. 이를 세션 하이재킹 공격이라고 한다.
해결책으로는 HTTPS 프로토콜 사용과 세션에 만료 시간을 넣어주는 것이 있다

뿐만 아니라 서버에서 세션 저장소를 사용하기 때문에 추가적인 저장공간을 필요로 한다

이러한 단점들을 극복하기 위해 JWT가 등장했다.
JWT는 Json Web Token의 약자로 인증에 필요한 정보들을 암호화 시킨 토큰을 말하며, Access Token으로 사용된다.

JWT를 생성하기 위해서는 Header, Payload, Verify Signature 객체를 필요로 한다

📌 Access Token을 통한 인증

JWT는 JSON Web Token의 약자로 인증에 필요한 정보들을 암호화시킨 토큰을 말하며, Access Token으로 사용된다
JWT를 생성하기 위해서는 Header, Payload, Verify Signature 객체가 필요하다

JWT 구성요소

Header는 토큰의 타입을 나타내는 typ과 암호화할 방식을 정하는 alg로 구성되어 있다

{
  'alg': 'HS256',
  'typ': 'JWT'
}

Payload

Payload는 토큰에 담을 정보를 포함
여기서 하나의 정보 조각을 클레임으로 부른다. 클레임의 종류로는 Registered, Public, Private로 3가지가 존재한다
보통 만료 일시, 발급 일시, 발급자, 권한정보 등을 포함한다

{
  'sub': '1234567890',
  'name': 'John Doe',
  'admin': true,
  'iat': 1516239022
}

Verify Signature

Verify Signature는 Payload가 위변조되지 않았다는 사실을 증명하는 문자열이다
Base64 방식으로 인코딩한 Header, Payload 그리고 SECRET KEY를 더한 후 서명된다

HMACSHA256 {
  base64UrlEncode(header) + '.' +
  base64UrlEncode(payload),
  your-256-bit-secret
}

완성된 토큰


Header, Payload는 인코딩될 뿐, 따로 암호화되지 않는다
따라서 Header, Payload는 누구나 디코딩하여 확인할 수 있기에 정보가 쉽게 노출될 수 있다. 하지만 Verify Signature는 SECRET KEY를 알지 못하면 복호화할 수 없다.

만약에 해커가 사용자의 토큰을 훔쳐 Payload의 데이터를 변경하여 토큰을 서버로 보낸다면, 서버에서 Verify Signature을 검사하게 된다. 여기서 Verify Signature는 해커의 정보가 아닌 사용자의 정보를 기반으로 암호화되었기 때문에 해커가 변경한 정보로 보낸 토큰은 유효하지 않은 토큰으로 간주한다. 이를 통해 사용자의 SECRET KEY를 알지 못하면 토큰을 조작할 수 없다는 것을 알 수 있다

인증순서

정리하면 아래와 같다

  1. 사용자가 로그인한다

  2. 서버에서 계정정도를 읽어 사용자를 확인한 후, 사용자의 고유한 ID를 부여한 후 Payload에 정보를 넣는다

  3. JWT의 유효기간도 설정한다

  4. SECRET KEY를 통해 암호화된 AccessToken을 HTTP 응답헤더에 실어 보낸다

  5. 사용자는 Access Token을 받아 저장한 후, 인증이 필요한 요청마다 토큰을 HTTP 요청헤더에 실어 보낸다

  6. 서버에서는 해당 토큰의 Verify Signature를 SECRET KEY로 복호화 한 후, 조작 여부 및 유효기간등을 확인한다

  7. 검증이 완료되면 Payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져온다

복호화 또는 디코딩
부호화된 정보를 부호화되기 전으로 되돌리는 처리 혹은 그 처리 방식

장단점

장점

  1. 세션과 쿠키를 통한 인증은 별도의 세션 저장소의 관리가 필요하나 JWT는 발급 후 검증만 거치면 되기에 추가 저장소가 필요없다

  2. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능해 확장성이 뛰어나다

단점

  1. JWT는 한 번 발급되면 유효기간이 완료될 때까지는 계속 사용이 가능하며 중간에 삭제가 불가능하다. 따라서 해커에 의해 정보가 털린다면 대처할 방법이 없.

    해결책으로는 Refresh Token을 추가적으로 발급하여 해결하는 방식

  2. Payload 정보가 디코딩하면 누구나 접근할 수 있기에 중요한 정보들을 보관할 수 없다

  3. JWT의 길이가 길기 때문에, 인증 요청이 많아지면 서버의 자원낭비가 발생

📌 Access Token + Refresh Token을 이용한 인증

Access Token을 이용한 인증 방식의 문제는 해커에게 탈취당할 경우 보안에 취약하다는 점이다
토큰의 유효기간을 짧게 하면 사용자는 로그인을 자주 해야해서 번거롭고, 길게 하면 보안이 취약해지기 때문에 이를 해결하고자 나온 것이 Refresh Token이다

Refresh Token은 Access Token과 같은 형태인 JWT이다.
Refresh Token은 Access Token보다 긴 유효기간을 가지고, Access Token이 만료됐을 때 새로 발급해주는 열쇠로 작용한다

리프레시 토큰의 저장과 관련해서는 다양한 해결법이 있다고 한다.
한 가지 예시로 Refresh 토큰을 토큰 자체로 반환하지 말고 DB에 저장된 곳의 인덱스(정수 or 해시값)만 반환한다면, 클라이언트 측에서는 무의미한 인덱스 숫자만 알게 되는 것이기에 보안적으로 조금 더 좋다고 한다. 이외에도 로컬 스토리지에 저장하기보다는 쿠키에 HttpOnly 를 통해서 전달하면 XSS 공격을 막을 수 있다고 한다.

인증순서

정리하자면 아래와 같다

  1. 사용자가 로그인한다

  2. 서버에서 회원 DB에서 값을 비교한다

  3. 로그인이 완료되면 Access Token과 Refresh Token을 발급해 HTTP 응답헤더에 보낸다. 일반적으로 회원 DB에 Refresh Token을 저장해 둔다

  4. 사용자는 Refresh Token을 안전한 장소에 저장 후, Access Token을 HTTP 요청 헤더에 실어서 보낸다.

  5. 서버는 Access Token을 검증하여 이에 맞는 데이터를 보낸다

  6. 시간이 지나 Access Token이 만료되면, 사용자는 이전과 동일하게 Access Token을 HTTP 요청 헤더에 넣어 보내지만 서버는 Access Token이 만료됨을 확인하고 권한 없음을 신호로 보낸다

  7. 사용자는 Refresh Token 과 Access Token을 HTTP 요청 헤더에 실어 보낸다

  8. 서버는 받은 AccessToken을 검증한 후, HTTP 요청 헤더의 Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token과 비교한다. Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 발급해준다

  9. 서버는 새로운 Access Token을 HTTP 응답 헤더에 실어 다시 API 요청을 진행한다

장단점

장점

Access Token의 유효 기간이 짧기 때문에, 기존의 Access Token만을 이용한 인증보다 안전하다

단점

구현이 복잡
Access Token이 만료될 때마다 새롭게 발급하는 과정에서 서버의 자원 낭비가 생김

📌 OAuth2.0

  • OAuth는 외부서비스의 인증 및 권한부여를 관리하는 범용적인 프로토콜이다

이러한 OAuth2.0에서는 다양한 인증 방법이 있는데 그 중에서 이번 포스팅에서는 Authroization code grant를 활용하는 방식에 대해 살펴볼 것이다

OAuth2.0의 다양한 인증방식

  1. Authorization Code Grant: 이 방법은 클라이언트가 사용자 인증을 위해 서버에 요청을 보내고, 서버는 사용자를 인증하고 권한 부여를 요청한다. 서버는 권한 승인 코드(authorization code)를 반환하고, 클라이언트는 이를 사용하여 액세스 토큰(access token)을 요청하는 방법이다
  2. Implicit Grant
  3. Resource Owner Password Credentials Grant
  4. Client Credentials

JWT의 활용법

JWT는 OAuth2.0과는 별개의 인증 프로토콜인데, OAuth 2.0에서는 보안 토큰으로 JWT를 사용하여 인증을 처리하는 방식이 많이 사용된다. 이 때, JWT는 Bearer Token 방식으로 HTTP 요청 헤더에 담아서 사용된다.
이러한 JWT Bearer Token은 OAuth 2.0 인증 프로토콜에서 사용되는 중요한 보안 기술 중 하나이다.

OAuth1.0과 OAuth2.0의 차이점

  1. 모바일에서도 사용이 용이
  2. 반드시 HTTPS를 사용하므로 보안이 강화됨
  3. Access Token의 만료 기간이 생김

주요 용어

  • Resource Owner: 개인 정보의 소유자를 가리킨다. 유저 A가 이에 해당한다.

  • Client: 제 3의 서비스로부터 인증을 받고자 하는 서버다. 직접 개발한 웹 사이트 X가 이에 해당한다.

  • Resource Server: 개인 정보를 저장하고 있는 서버를 의미한다. 구글이 이에 해당한다.

  • Client IDResource Server에서 발급해주는 ID. 웹 사이트 X에 구글이 할당한 ID를 알려주는 것이다.

  • Client SecretResource Server에서 발급해주는 PW. 웹 사이트 X에 구글이 할당한 PW를 알려주는 것이다.

  • Authorized Redirect Uri: Client 측에서 등록하는 Url.
    ex. http://localhost:8080/login/oauth2/code/naver

인증순서


정리하자면 아래와 같다

  1. 먼저 resource Owner 가 client에게 인증요청을 한다.

  2. client는 authorization request를 통해 resource owner에게 인증할 수단(authorization request)를 보낸다

    ex) https://accounts.google.com/client_id=123&scope=profile,email&redirect_uri=http://localhost

  3. client는 인증이 완료되면 해당 권한 증서(authorization code, 권한 부여 코드)를 authorization server에 보낸다.

    ex)
    http://localhost:8080/login/oauth2/code/google
    등록 ID가 "google"인 클라이언트 애플리케이션이 OAuth 2.0 인증 서버에서 인가 코드를 수신하는 데 사용

  4. authorization server는 권한 증서를 확인 후, 유저가 맞다면 client에게 access Token, refresh Token, 그리고 유저의 정보를 발급해준다.

  5. client는 해당 access Token을 DB에 저장하거나 resource owner에게 넘긴다

  6. resource owner가 resource server에 자원이 필요하면 client는 access token을 담아 resource server에 요청한다

  7. resource server 는 access token이 유효한 지 확인 후, client에게 자원을 보낸다

  8. 만일 access token이 만료됐거나 위조되었다면, client는 authorization server에 refresh token을 보내 access token을 재발급 받는다

  9. 그 후 다시 resource Server에 자원을 요청한다

참고

https://velog.io/@gusdnr814/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9D%B8%EC%A6%9D-4%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95
https://velog.io/@tmdgh0221/Spring-Security-%EC%99%80-OAuth-2.0-%EC%99%80-JWT-%EC%9D%98-%EC%BD%9C%EB%9D%BC%EB%B3%B4

profile
기록용

0개의 댓글