[WAFFLE] 03-2 Authentication & Authorization
Published:
In this post, concepts of user authentication and authorization will be provided.
Authentication and Authorization

- 인증 : 사용자가 누구인지 확인하는 과정
- 인가 : 사용자가 특정 자원에 접근할 권한이 있는지 확인하는 과정
Cookie and Session

- 쿠키 : 클라이언트(브라우저 등)에 저장되는 작은 데이터 조각
- HTTP State Management Mechanism (RFC 6265) 쿠키의 명세를 정의하는 RFC
- 기본적으로 HTTP는 stateless 한 프로토콜임. 즉, 전에 어떤 요청을 몇 번 했든, 다음 요청과는 독립적. 근데 거기에 state를 제공하기 위한 방법으로 쿠키라는 기술 존재.
- 세션 : 서버에 저장되는 클라이언트 정보
- 세션 ID는 클라이언트에 저장되며, 그 저장 방식으로 쿠키를 사용할 수 있음.
- 세션 정보는 인증 서버에 저장됨.
- Stateless : 상태를 저장하지 않는 것
- 기본적으로 HTTP 프로토콜은 Stateless
- 즉, 이전 요청과 다음 요청이 서로 독립적. 하지만 유용한 애플리케이션은, 어딘가에 정보를 저장해야 함(서버 vs 클라이언트)
- 만약 서버가 Stateless 하고 싶다면, 클라이언트에서 상태를 저장하고 매 요청마다 보내줘야함.
Session-Based Authentication

- 세션 기반 인증은 서버에 세션 정보를 저장하고, 클라이언트에 세션 ID를 전달함.
- 즉 양쪽 모두 Stateful
- 브라우저는 다음 요청을 할 때, 한 번 인증이 된 뒤로부터는 세션 ID만 보내주면 새로 로그인할 필요 없이 서버에서 ‘방금 전 로그인했던 걔네’ 하고 그 사람이 접근할 수 있는 리소스를 보내줌.
- 세션 ID를 이용해서 서버에 저장된 세션 정보를 조회하고, 사용자를 식별함.
- 세션 정보를 가장 간단하게는 서버에 인메모리 자료구조를 만들어 거기에 저장해 놓을 수 있음. 만약 천만명이 이용하는 서비스라면 상당히 많은 자원이 세션 정보 저장을 위해 필요함. 또한 천만명이 이용하는 서비스라면 서버 한대로는 부족해 여러 서버를 운용하는데 이때 각 서버가 모두 천만명의 세션 정보를 가지고 있어야 함. 결국엔 세션 정보만 저장하기 위한 db를 따로 두게 됨. 그러나 db를 갖다오는 것이 비용이 큰 작업임. 이것이 Session-Based Authentication의 단점.
Token-Based Authentication

- 토큰은 사용자의 정보를 담은 문자열. 토큰 기반 인증은 클라이언트에 세션 ID가 아니라, 토큰을 저장함.
- 서버 입장에서 Stateless
- 매번 요청할 때마다 해당 토큰을 저장하면 서버는 세션 db를 검사할 필요 없이 토큰을 보고(특정 알고리즘을 이용해 토큰을 검증하고 사용자 식별) 유저의 권한에 대해 알 수 있기 때문에 바로 그 사람이 가질 수 있는 리소스를 전달해줌.
- 당연히 db를 거치지 않기 때문에 속도도 빠르고 각 서버간 공통된 메모리를 필요로 하지 않아 좋음.
- 탈취를 당했을 때 토큰에 들어있는 사용자 정보를 해커들이 까보기 쉬움. 토큰 인증에는 민감한 데이터를 넣으면 안됨.
- 또한 토큰에는 더 많은 정보가 들어있기 때문에 하나 하나의 요청이 무거워지게됨.
JWT

- JSON Web Token은 가장 보편적인 토큰기반 인증 방식
- 헤더, 페이로드, 서명으로 구성됨. 마침표로 구분되는 3개의 문자열.
- 헤더 : base64 인코딩된 토큰의 타입과 알고리즘
- 페이로드 : base64 인코딩된 사용자 정보(claim의 집합). 유저 이름, 언제 발급되었고 언제까지 유용한지, 어떤 리소스를 볼 수 있는 권한이 있는지 등.
- 서명 : 헤더와 페이로드를 암호한 값 (HS256, RS256이 가장 보편적)
- 서명이 없다면 인코딩된 헤더와 페이로드를 디코딩만 하면 바로 값을 알 수 있어 해커가 바로 까볼 수 있음. 또한 인코딩만 해서 보내면 되므로 서버한테 내가 다른 사람이라 속이기 쉬움.
- 암호화에는 비밀키가 필요한데 비밀키는 서버만 가지고 있기 때문에 남들이 함부로 JWT를 만들거나 쉽게 까볼 수 없음.
HTTP/1.1 200 OK
Set-Cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c; HttpOnly; Secure
GET /me HTTP/1.1
Cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- JWT 응답과 요청. JWT를 클라이언트에 저장하는 가장 쉬운 방법은 쿠키를 이용하는 것.
- 서버에서 응답을 보낼 때 Set-Cookie라는 헤더를 보내면 브라우저는 자동으로 쿠키 데이터를 파싱에서 브라우저 내부에 저장함.
- 다음부터는 동일한 서버에 요청할 때 자동으로 토큰이 날아가게 됨. 그 토큰을 보고 서버는 유저를 식별함.
- Set-Cookie를 이용해 서버에서 등록할 수 있고 프론트엔드 개발자가 직접 자바스크립트를 동작해서 등록할 수도 있음.
- 서버에서 보내는 경우 HTTP only 와 secure 설정을 해두는게 보안상 좋음.
HTTP/1.1 200 OK
Content-Type: application/json
{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"}
GET /me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 쿠키를 이용하는 것이 아니라 클라이언트가 직접 저장하게 하는 방식이 있음. Set-Cookie가 아니라 Body를 통해 토큰을 보내주게 되면 브라우저 웹페이지에서 메모리로 가지고 있든, 로컬 스토리지나 세션 스토리지에 저장을 해두던, 저정해두었가 다음 요청에 헤더로 전달.
- 이런 경우, Authorization이라는 헤더를 가장 보편적으로 사용. Bearer prefix 뒤에 붙여 보내는 것이 표준.
- 쿠키와 헤더를 사용하는 방법은 각각 장단점이 있으므로, 상황에 맞게 사용하면 됨.
- 쿠키는 자동으로 날아가므로 글자 스크립트 공격에 취약할 수 있음.
- 헤더는 클라이언트에서 해당 토큰을 직접 조회할 수 있으므로 토큰을 탈취당할 위험이 있음. 쿠키는 HTTP only를 설정하면 클라이언트에서 절대 볼 수 없음.

Leave a Comment