Session Based Authorization
서버는 클라이언트로부터 유저가 로그인 시 입력한 아이디와 비밀번호를 전달받아 이 데이터가 db에 존재하는지 확인한다. 만약 존재한다면, 서버는 클라이언트의 쿠키에 sessionID
를 저장하고, 서버에도 sessionID
를 저장한다. 따라서, 클라이언트가 요청을 보낼때마다 서버 또는 데이터베이스에 요청을 보내 유효한 sessionID
인지 확인한다.
만약, 저장된 sessionID가 많다면 시간이 오래 걸릴 수 있을 것 같다. 그리고 서버 확장의 관점에서 본다면, 로그인을 요청해서 sessionID를 저장하고 있는 서버로만 요청이 가야한다는 한계를 갖는다. 이를 해결하는 방법으로는 우선 sticky session이 있다.
유저1이 1번 서버에 세션을 생성했다면, 앞으로 유저1의 모든 요청은 1번 서버로 보내집니다. 이를 가능하게 하는 것이 바로 load balancer
이다. 로드 밸런서는 요청한 브라우저에 쿠키를 생성하여 해당 쿠키를 이용해 각 서버로 요청을 리다이렉트 시켜주는 역할을 한다.
장점
유저는 하나의 고정된 세션으로만 요청을 보내므로 세션의 정합성이 보장된다.
단점
session clustering
은 여러개의 서버가 하나처럼 작동하도록 하는 기술이다. sticky session
에서는 하나의 서버가 죽으면, 해당 서버를 사용하는 유저 정보를 모두 잃어버린다는 단점이 있었는데, 이는 다른 서버들이 있기 때문에 문제가 생기지 않는다.
session clustering은 사용하는 WAS에 따라 다른 방법이 적용되기 때문에 프로젝트에서 어떤 WAS를 사용하는지 살펴보아야한다.
여기서 WAS(Web Application Server)란, 사용자의 입력을 받아 서버에서 무언가를 처리하고 그 결과를 보여주는 동적인 데이터를 처리 웹서버이다.
가장 대표적인 WAS인 톰캣이 지원하는 session clustering 방식에 대해 알아보도록 하자.
하나의 세션에 변경이 일어나면 다른 모든 세션에 복제되는 것을 말한다. 이는 소규모 클러스터에 적합한 방식이다.
각 서버의 세션 저장소에 세션정보를 저장하는 것이 아니라, 독립된 세션 저장소에 세션 정보를 저장한다. 따라서 여러 서버들이 이 독립된 세션 저장소에서 세션정보를 읽어온다.
MySQL
, oracleDB
등의 RDBMS와 영속성이 필요하지 않다면 memcached
를, 영속성이 필요하다면 redis
를 사용할 수 있다. Token Based Authorization (JWT)
마찬가지로, 유저정보가 db에 존재한다면, 서버는 클라이언트의 쿠키에 토큰을 저장한다. 따라서 클라이언트는 다음 요청부터는 토큰을 실어서 요청을 보낸다. 따라서 서버는 이 토큰이 유효한지 확인한다. session based authorization과는 다르게 서버에서 session id를 관리하고 있지 않다는 특징을 갖는다.
생성된 토큰은 헤더, 페이로드, 시그니처로 구성되어있다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
헤더
암호화할 알고리즘과 토큰의 타입(이 경우는 JWT)이 정의되어있다. Base64Url로 암호화되어 있다.
페이로드
토큰을 만드는데 사용되는 데이터가 저장되어있다. 헤더와 마찬가지로 Base64Url로 암호화되어 있으므로 디코딩이 가능하기 때문에 비밀번호, 카드번호 등의 중요한 정보는 저장해서는 안된다.
시그니처
헤더와 페이로드를 설정한 비밀키로 암호화된 값이 저장된다. 만약 토큰이 탈취되어 페이로드의 토큰 유효기간을 임의로 변경한다면 Base64URL로 새롭게 암호화된 페이로드는 다른 값을 가진다. 하지만, 시그니처는 비밀키를 모르면 변경할 수 없다. 따라서, 이 토큰은 더이상 유효하지 않다.
cookies | local storage | session storage | |
---|---|---|---|
capacity | 4kb | 10mb | 5mb |
browsers | HTML4/ HTML5 | HTML5 | HTML5 |
accessible from | Any window | Any window | Same tab |
expires | Manually set | NEVER (unless logout or manually remove) | On tab close |
storage location | Browser (but sent to server) | Browser only | Browser only |
자바스크립트를 사용해서 제어가 가능하다.
자바스크립트로 값을 쉽게 저장하고 가져오고, 삭제할 수 있어 편하다.
XSS 공격에 취약하다.
로컬스토리지에 저장된 액세스 토큰은 Bearer 스키마를 사용해 Authorization 헤더에 담아 HTTP 요청을 보내야한다. Authorization Bearer ${access_token}
서버에서는 이 내용을 파싱해서 토큰을 확인한다.
웹스토리지와 마찬가지로 자바스크립트를 통해 제어가 가능하다.
쿠키의 httpOnly 옵션을 true로 설정하면, 자바스크립트로 쿠키 접근을 제한할 수 있어 로컬 스토리지에 비해 XSS 공격에 대한 보안을 높일 수 있다.
쿠키는 해당 도메인에 대해 자동으로 모든 HTTP 요청에 포함되어 보내진다.
웹스토리지와 다르게 매 요청마다 헤더를 직접 설정해주지 않아도 된다.
쿠키는 4kb로 사이즈가 제한된다.
CSRF 공격에 취약하다.
CSRF가 발생하는 시나리오
A.com에서 생성한 쿠키는 B.com에서는 확인할 수 없다. A.com으로 보내는 HTTP 요청들은 브라우저가 자동으로 쿠키를 헤더에 담아서 보낸다. HttpOnly 옵션을 설정한 쿠키의 경우 자바스크립트로도 쿠키를 얻을 수 없다. 그렇다면 어떻게 쿠키를 가로챌 수 있을까?
예를 들어, 해커가 hacking.com이라는 사이트를 만들어서 A.com을 사용하는 유저에게 해당 해킹 사이트로 접속을 유도하는 이메일을 보냈다고 해보자. 해킹 사이트의 HTML 페이지는 <img src= "https://A.com/travel_update?.src=Korea&.dst=Hell">
라는 이미지 태그를 갖는다. 따라서, 유저가 해당 사이트에 접속했다면, 브라우저는 이미지 파일을 받아오기 위해 도착지를 임의로 변경하는 공격용 URL을 열 것이다. 브라우저에 A.com의 액세스 토큰이 저장되어있다면, 이 쿠키를 담아서 요청을 보낼 것이다.
결국, 유저가 알지 못하는 사이에 도착지가 변경되는 문제가 발생한다.
CSRF를 해결하는 방법
다른 사이트에서 자사의 쿠키를 가지고 인증하여 생기는 보안 이슈는 "다른 사이트"의 접근을 제한하는 것과 "자사의 쿠키 사용"을 제한하는 것으로 해결 할 수 있다.
reference