O
OpenLog
Search
Log in
Back to Suggestions
New Suggest
Suggest edit for "JWT란 무엇일까?"
Suggestion title
Description
</>
Edit
Preview
## Summary ## Reason
</>
Edit
Preview
JWT는 JSON Web Token의 약자로 당사자 간에 정보를 JSON 객체 형태로 안전하게 전송하기 위한 표준이다. 위변조가 불가능한 서명된 JSON 데이터 조각으로 주로 사용자의 인증 및 권한 부여 정보를 서버와 클라이언트 간에 주고받을 때 사용된다. JWT의 가장 큰 특징은 stateless다. 기존의 세션 방식은 서버의 메모리나 DB에 어떤 사용자가 로그인했다는 기록을 남겨야 했지만, JWT는 토큰 그 자체에 사용자의 정보와 유효성 검증 수단이 모두 포함되어 있다. 따라서 서버는 별도의 저장소를 조회하지 않고 토큰의 서명만 검증하면 이 토큰의 유효성을 즉시 판단할 수 있다. #### JWT의 구조 JWT는 점으로 구분된 세 부분의 문자열로 구성된다. ```jwt aaaaaa.bbbbbb.cccccc (Header.Payload.Signature) ``` Header는 토큰의 메타데이터로 다음과 같은 내용을 담고 있다. ```json { "alg": "HS256", // 서명에 사용된 알고리즘 "typ": "JWT" // 토큰의 타입 } ``` 이 JSON을 Base64URL로 인코딩하면 JWT의 첫 번째 부분이 된다. 두 번째 부분인 Payload는 실질적으로 전달하려는 데이터(claim)가 담긴다. 표준 클레임으로는 `sub`(주제/ID), `iat`(발급시간), `exp`(만료시간) 등이 있고 개발자가 임의로 `userId`나 `email`,`role`같은 사용자 지정 클레임을 넣을 수도 있다. Base64 인코딩 방식은 해시함수처럼 동일한 길이의 결과값을 뽑는 방식이 아니고 데이터가 늘어나면 길이도 비례해서 늘어나기 때문에 필요한 최소한의 정보만 넣는 것이 원칙이다. 여기서 페이로드는 암호화된게 아니라 단순히 인코딩된것이므로 중요정보를 넣지말아야한다. ```json { "sub": "1234567890", "name": "John Doe", "role": "admin" } ``` 마지막 부분인 Signature는 토큰이 위변조되지 않았음을 증명하는 중요한 파트다. $$\text{Signature} = \text{HMAC-SHA256}( \text{EncodedHeader} + "." + \text{EncodedPayload}, \ \text{SecretKey} )$$ Header와 Payload, 그리고 서버만 아는 비밀키를 붙여 헤더에 명시된 알고리즘에 넣어 고정된 길이의 서명을 생성한다. 만약 해커가 페이로드의 내용을 수정하면 해시값이 완전히 달라지게 되고 해커는 서버의 비밀키를 모르기 떄문에 올바른 Signature를 만들어낼 수 없게되므로 서버는 서명이 일치하지 않는 것을 보고 요청을 거부하게 된다. ### OAuth란 무엇일까? OAuth (Open Authorization)는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로 사용되는 개방형 표준 인증 프로토콜이다. 쉽게 말해서 애플리케이션이 사용자를 대신해 구글이나 페이스북 같은 서버의 자원에 접근할 수 있도록 권한을 위임받는 기술이라고 이해할 수 있을 것 같다. #### OAuth의 핵심 구성 요소 OAuth 시스템이 동작하기 위한 4가지 주체가 있다. + Resource Owner (사용자): 데이터의 소유자로 애플리케이션에 자신의 데이터 접근 권한을 승인하는 주체 + Client (클라이언트/앱): 사용자의 자원에 접근하려는 애플리케이션 + Authorization Server (인증 서버): 사용자의 신원을 확인하고 클라이언트에게 Access Token을 발급하는 서버 + Resource Server (자원 서버): 사용자의 실제 데이터를 보관하고 있는 서버로 올바른 Access Token이 제시되면 데이터를 제공함 여기서 토큰은 실제 자원에 접근하기 위한 열쇠로 보통 1시간 내외의 짧은 수명을 가지고 있다. 클라이언트가 Resource Server에 요청을 보낼 때 HTTP 헤더에 담아서 보내게 된다. Access Token의 짧은 수명을 커버하기 위해 Refresh Token이라는 것도 존재한다. Access Token이 만료되었을 때, 사용자의 재로그인 없이 새로운 Access Token을 발급받기 위해서 사용되고 수명이 수일~ 수개월로 길며 Resource Server에는 보내지 않고 Authorization Server랑만 교환된다. #### OAuth 흐름 가장 보편적으로 사용되는 Authorization Code Grant 방식을 기준으로 어떻게 서비스가 사용자의 정보를 Google OAuth를 통해 가져오게되는지 흐름을 알아보자. 먼저 사용자가 클라이언트 앱에서 'Google로 로그인' 버튼을 누르면 클라이언트는 사용자를 인증 서버(구글 로그인 페이지)로 리다이렉트 시킨다. 이때 클라이언트는 `client_id`,`redirect_uri`,`scope`,`reponse_type=code`등의 파라미터를 URL에 포함해 인증 서버에 전달한다. 사용자는 인증 서버 화면에서 로그인을 수행하고, 클라이언트가 요청한 권한을 승인할지 결정한다. 승인 버튼을 누르면 인증 서버는 사용자의 신원을 확인한뒤 파라미터로 전달된 `redirect_uri`(클라이언트의 프론트가 될 수도 있고 백 주소가 될 수도 있음)로 사용자를 이동시키며 이 과정에서 클라이언트에게 Authorization Code(일회용)를 전달한다. 클라이언트는 전달받은 Authorization Code를 가지고 이번엔 사용자를 거치지 않고 직접 인증서버에 Authorization Code와 함께 자신의 비밀키인 `Client Secret`을 인증 서버에 전송한다. 이는 방금 전에 사용자가 승인한 앱이 바로 클라이언트, 자신이며 그 증거로 Authorization Code를 보여주는 것이라고 이해할 수 있다. 인증서버는 `Authorization Code`와 `Client Secret`이 유효한지 검증한 후 클라이언트에게 Access Token(및 Refresh Token)을 발급하여 응답하고, 클라이언트는 사용자의 권한을 획득하게 된다. 클라이언트는 이렇게 발급받은 Access Token을 가지고 API 요청의 HTTP Header에 포함해 자원 서버에 데이터를 요청하게 된다. 자원 서버는 토큰의 서명과 유효기간을 검증한 뒤 요청된 자원을 클라이언트에게 반환한다. 그럼 OAuth로 회원가입을 하고 로그인하는 경우는 어떻게 그 사용자인지 검증을 할 수 있는건지 감이 올 것이다. OAuth로 회원가입을 할 때 자원서버로부터 받아온 값을 내 서버의 DB에 저장을 해두고, 나중에 사용자가 OAuth로 로그인을 할 때 자원서버로부터 동일한 과정으로 정보를 받아와 이 두 정보를 비교하는 게 핵심 로직이다. 자원 서버가 반환되는 실제 데이터는 이런 식이다. ```json { "sub": "105432895240293847230", // 고유 식별자인 sub를 auth에 사용한다. "name": "홍길동", "given_name": "길동", "family_name": "홍", "picture": "https://lh3.googleusercontent.com/a-/AOh14Gj...", "email": "gildong@gmail.com", "email_verified": true, "locale": "ko" } ``` 사실 이런 세부적인 로직은 Spring Security같은 라이브러리가 처리해주긴 한다. (이 글을 쓸 당시에는 NestJS로 개발했기도 하고 라이브러리의 존재를 잊어서 전부 직접 구현했었는데..)
Files changed
No changes yet.
Cancel
Submit suggestion