- Connectionless
- 클라이언트와 서버가 요청과 응답을 한 번 주고 받으면 연결을 끊어버리는 특성이 있다. 클라이언트가 request를 서버로 보내면 서버는 클라이언트가 보낸 request에 맞게 response를 보내고 연결을 끊는다.
- Stateless
- 연결을 끊는 순간, 사용자와 서버의 통신이 끝나며 상태 정보는 유지하지 않는 특성이 있다. 브라우저를 닫을 때마다 로그인이 풀리지 않게, ‘상태’를 유지하는 방법은 1)쿠키와 세션 2)토큰기반 방식 이 있다.
- 쿠키
- 클라이언트에 저장되는 키-값으로 구성된 작은 데이터 조각
- 서버가 클라이언트에 정보를 전달할 때 저장하고자 하는 정보를 응답헤더(Cookie)에 저장하여 전달한다.
- 쿠기에 담긴 데이터는 브라우저에서 관리됨(보통 만료 날짜를 서버에서 설정한다.)
- 서버와 요청 응답으로 인해 쿠키가 저장되면 다음 요청은 쿠키에 담긴 정보를 이용해 참조한다.
- 이름, 값, 만료 날짜 등으로 구성
- 쿠키 동작 방식
- 클라이언트가 로그인 요청
- 서버에서 쿠키 생성 후 클라이언트로 전달
- 클라이언트가 서버로 요청을 보낼 때 쿠키 전송
- 쿠키를 이용해 유저 인증을 진행
- 단점
- 웹 브라우저 간 Cookie에 대한 지원 형태가 다르기 때문에 브라우저 간 공유가 불가능하다.
- Cookie사이즈는 4kb로 제한되어있어 많은 양의 데이터를 담을 수 없다.
- 세션
- 쿠키를 기반으로 하지만 클라이언트에 저장하는 쿠키와 다르게 서버에 저장하여 관리→ 쿠키에 중요정보를 담지 않고, 인증을 위한 별개의 정보를 세션 저장소에 저장한다.
- 서버에서는 클라이언트를 구별하기 위해 각각의 세션ID를 클라이언트 마다 부여하고, 클라이언트가 종료되기 전까지 유지한다.
- 세션ID: 웹 서버 메모리에 저장되는 클라이언트에 대한 유니크한 ID(서버 또는 데이터 베이스에 저장)
- 클라이언트에 저장하는 쿠키보다는 보안이 좋다.
- 세션 동작 방식
- XSS(Cross Site Scripting): 쿠키는 클라이언트에서 자바 스크립트로 조회할 수 있기 때문에, 공격자들이 자바스크립트로 쿠키를 가로채고자 시도를 한다.
- 해결 방법: HTTP Only Cookie 또는 secure cookie를 사용한다.
- Set-Cookie: 쿠키명=쿠키값;path=/; secure
- http프로토콜은 언제든지 패킷을 중간에 가로챌 수 있기 때문에 https 프로토콜을 사용하여 데이터를 암호화 해 통신한다. secure 설정을 하면 https프로토콜이 아닌 경우 쿠키를 전송하지 않게 된다.
- Set-Cookie: 쿠키명=쿠키값;path=/; HttpOnly
- 클라이언트에서 자바 스크립트로 쿠키를 조회할 수 있는데, 해당 옵션을 활성화하면 브라우저에서 쿠키에 접근할 수 없으므로 XSS와 같은 공격으로부터 안전하다.
- Set-Cookie: 쿠키명=쿠키값;path=/; secure
- 보통은 HTTP관련 라이브러리에 적혀있는 대로 axios.defaults.withCredentials=true;
- 해결 방법: HTTP Only Cookie 또는 secure cookie를 사용한다.
- 세션 하이재킹의 공격은 세션 유효시간으로 예방할 수 있다.
- 세션-쿠키 방식의 단점: 로그인 할 때마다 세션 id를 저장해야하기 때문에, 로그인 중인 유저의 수가 늘어나면 서버의 메모리가 과부화 될 가능성이 있다.
- 토큰 기반 인증 방식: JWT토큰->인증은 토큰 기반 인증 서버를 통해서 하게 하고, 서버는 stateless하게 내버려둔다.
- 요청>토큰생성>이후 사용자가 토큰을 헤더(authorization 키에 넣어서 요청. 이 토큰을 기반으로)
- 헤더, 페이로드, 서명으로 이루어져 있으며 JSON객체로 인코딩된다. 메세지 인증, 암호화에 사용한다.
- Header: 어떤 방법의 서명 알고리즘을 사용할 것인가에 대한 정보
- Payload: 데이터, 토큰 발급자, 토큰 유효 기간(인증이 필요한 최소한의 정보만)
- Header,Payload는 누구나 디코딩하여 내용을 확인할 수 있기 때문에, 유저의 비밀번호 같은 중요한 내용은 포함하지 않는다.
- Signature: 헤더에 정의된 알고리즘으로 인코딩된 헤더와 페이로드를 합친 값, 그리고 비밀키를 기반으로 생성된 서명 값
- Secret key를 알아야 signature를 복호화 할 수 있기 때문에, 토큰을 변조하였더라도 유효하지 않은 토큰은 검증이 가능하다.
- 토큰 동작 방식
- 클라이언트가 로그인 요청을 한다.
- 서버에서 유저의 고유한 ID와 다른 인증 정보들을 payload에 담는다.
- JWT의 유효기간과 옵션을 설정한다.
- Secret Key를 이용해 토큰을 발급한다.
- 발급된 토큰은 클라이언트에 쿠키 또는 로컬 스토리지 등에 저장하여 요청을 보낼 때마다 같이 보낸다.
- 서버는 토큰을 Seceret Key로 복호화하여 검증하는 과정을 거친다.
- 검증이 완료되면 대응하는 데이터를 보내준다.
- 장점
- 사용자가 인증되면 사용자는 모든 시스템에서 사용할 수 있는 보안 토큰을 받는다. 즉, 단일 엔드 포인트를 생성해서 다른 모든 서버 간의 API 상호 작용을 인증할 수 있다는 점에서 좋다.
- 사용자 인증에 필요한 모든 정보는 토큰 자체에 포함하기 때문에 별도의 인증 저장소가 필요없다. 세션의 경우는 계속해서 저장해야 함. 확장성, 디버깅, 사이즈가 작다. jwt토큰 자체가 독립적이다.
- 단점:
- 더 많은 필드가 추가되면 토큰이 비대해져 트래픽에 영향을 준다.
- 탈취하여 디코딩하면 데이터를 볼 수 있다.
- 발급된 토큰은 삭제가 불가능하다. →Refresh Token
- RefreshToken
- Access Token만으로는 공격자가 요청하는 것인지 정상적인 클라이언트가 요청하는 것인지 알 수 없다. 그리고 Access Token은 언제든 탈취될 수 있다고 가정하기 때문에 중요 정보를 담아선 안된다. 따라서 AccessToken은 유효기간을 짧게 설정하고 Refresh Token의 유효기간은 길게 설정한다.
- 동작방식
- Access Token을 로컬스토리지 또는 세션스토리지에 저장하고, Refresh Token은 쿠키에 저장하고 보안 옵션(HTTP Only, Secure Cookie)들을 활성화한다. Refresh Token은 서버에도 저장되어 있어야 한다.
- RefreshToken이 탈취될 경우
- RefreshToken만 탈취되면 공격자는 탈취한 토큰으로 계속 Access Toeken을 생성해서 정상적인 사용자처럼 서버에 계속 요청할 수 있다.
- 이를 방어하기 위해 추가 검증로직을 사용해야한다.
- DB에 사용자와 Access Token, Refresh Token들을 매핑하여 저장한다.
- 정상적인 유저의 Access Token이 만료된 경우 AccessToken과 RefreshToken을 서버로 보내서 새 AT을 요청한다.→서버에서는 DB에 저장된 AT,RT쌍과 클라이언트가 보낸 토큰 쌍을 비교한다.→ 일치하면 새 AT을 발급한다.
- 공격자가 RefreshToken을 탈취한 경우, 공격자가 탈취한 RT으로 새 AT 생성을 요청한다. →AT이 없이 요청하면 공격으로 간주한다. → 서버에서 AT와 RT를 폐기한다.
- RefrestToken과 Access Token이 모두 탈취되면
- 공격자는 정상적인 유저처럼 요청하고 새로운 AccessToken을 발급받게되어 대응할 방법이 없다!