프로젝트를 진행하며 로그인 / 회원가입 부분을 담당하여 개발하게 되었습니다.
로그인을 구현하기 위해 공부한 내용을 정리하고 공유하고자 하는 목적으로 글을 작성합니다.
우선 사용자 로그인에 관한 이해를 위해 알아야 할 개념이 있습니다. 바로 인증과 인가 입니다.
✔ 인증(Authentication) 과 인가(Authorization)
인증(Authentication)
식별 가능한 정보로 서비스에 등록된 유저의 신원을 입증하는 과정
인가(Authorization)
인증된 사용자에 대하여 자원의 접근권한을 확인하는 것
개념만 정의한다면 추상적이라 구체적인 설명을 붙이자면
- 간단한 게시판 서비스가 있다고 가정하면 모든 사용자가 글을 읽을 수 있으나 글을 작성하려면 유저 정보가 필요합니다.
- 사용자는 로그인을 통해 서비스에 등록된 유저임을 서버에 알리고 글을 쓸 수 있는 권한을 얻게 됩니다.
- 이 과정에서 로그인을 통해 인증을 받게되고 서버로 부터 접근 권한을 인가 받게 되는 것입니다.
추가로, 유저는 자신의 글은 수정 / 삭제 할 수 있겠지만 다른 사람의 글은 수정 / 삭제 할 권한이 없을 것입니다.
즉, 인증과 인가란 웹 서비스에서 리소스를 적절한 사용자에게 전달 하고 공개하는 방법을 말합니다.
✔ 토큰 기반의 인증 방법
웹에서 사용자를 인증하는 보편적인 방법은 로그인을 통해 인증하는 것입니다.
로그인 구현 전략 중 하나는 쿠키와 세션을 이용하는 방법이 있습니다.
해당 글을 통해 쿠키-세션 방식의 로그인에 관해 알 수 있습니다. 간단히 정리하자면
- 쿠키-세션 방식의 경우 세션을 서버에 저장해야하므로 비효율적입니다.
- 서버 확장 시 서버간에 세션을 공유하기 어렵습니다.
이러한 단점을 해결하기 위한 방법으로 토큰 기반의 사용자 인증 방법을 사용할 수 있습니다.
∗ JWT(Json Web Token)?
토큰 이란 간단히 말하면 로그인 이후 서버가 만들어서 사용자에게 넘겨주는 문자열 입니다. 이 문자열은 사용자 정보가 암호화 되어있고 이 토큰을 이용하여 인증된 사용자인지 서버가 판단합니다.
이때 사용되는 토큰이 JWT(Json Web Token)이고 하나의 표준 인증 방식으로 이해할 수 있습니다.
∗ JWT 구조
JWT는 세 파트로 나누어지며, 각 파트는 점로 구분하여 xxxxx.yyyyy.zzzzz 이런식으로 표현됩니다.
순서대로 헤더 (Header), 페이로드 (Payload), 서명 (Sinature)로 구성합니다.
1) header
- 토큰의 종류와 해싱 알고리즘의 정보 이렇게 2가지 정보가 담겨 있습니다.
- typ: 토큰의 타입을 지정합니다. 바로 JWT를 말하는 것입니다.
- alg: 해싱 알고리즘을 지정합니다. 보통 HMAC-SHA256 혹은 RSA 가 사용되며, 이 알고리즘은 토큰을 검증 할 때 사용되는 signature 부분에서 사용됩니다.
2) payload
- 토큰의 내용물이 인코딩된 부분
- 즉, 토큰에 담을 정보가 들어 있는 부분입니다.
- 여기에 담는 정보의 한 ‘조각’ 을 클레임(Claim) 이라고 부르고, 이는 Json(Key/Value) 형태의 한 쌍으로 이뤄져있습니다. 토큰에는 여러개의 클레임들을 넣을 수 있습니다. (세부 클레임 사항은 검색을 통해 보는 것이 효율적 입니다.)
3) Signature
- 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드입니다.
- 헤더(Header)와 내용(Payload)의 값을 각각 BASE64로 인코딩하고, 인코딩한 값을 비밀키를 이용해 헤더에서 정의한 알고리즘으로 해싱을 하고, 이 값을 다시 BASE64로 인코딩하여 생성한다.
- secret key로 사용되는 일련의 문자열이라고 이해하면 쉽습니다.
위 사이트에서 토큰의 구조를 확인해 볼 수 있습니다.
✔ JWT 기반 로그인 동작 과정
- 사용자는 id(or email) 과 password를 입력하고 서버로 요청을 보냅니다.
- 서버는 db에서 회원을 조회하고 등록된 사용자인지 확인 합니다.
- 등록된 사용자라면 서버는 토큰을 생성하고 프론트로 토큰과 함께 응답을 보냅니다.
- 응답이 성공적으로 왔다면 로그인(인증)이 성공적으로 이루어 진 것이고 이후 요청에 token을 함께보냅니다.
- 서버는 로그인 이후의 요청에 함께 실려온 토큰을 검증하는 과정을 검증이 성공적으로 끝났다면 권한이 있는 사용자라고 생각하고 요청된 데이터를 응답해 줍니다. (인가)
프론트에서 응답 받은 토큰은 이후 요청시 Authorization header에 토큰 정보를 포함 하면 됩니다.
예를 들어
axios.defaults.headers.common.Authorization = `Bearer ${token}`; 이런식으로 설정 가능
✔ Access Token? Refresh Token?
기본적인 로그인 과정은 위 설명과 동일하나 한가지 주의 사항이 있습니다. token은 발급 시 유효기간 동안 되돌릴 수 없고 access token을 탈취당할 시 인증 정보가 모두 담겨있으므로 위험합니다.
이를 해결하기 위해 서버에서는 access token과 refresh token 두개의 토큰을 발행 합니다.
- Access Token : 말 그대로 인증된 유저인지 서버에서 검증하는 토큰 입니다. client 쪽에서 요청 헤더에 담아 보내는 토큰 입니다.
- Refresh Token : access token이 만료되거나 잘못된 토큰일 경우 refresh token을 사용해 유저를 검증합니다. 만약 refresh token을 검증하여 인증된 유저일 경우 access token을 재발행 해줍니다.
* 정리
access token의 유효기간을 짧게 설정하고 refresh token의 유효기간을 길게 설정하여 access token 만료시 refresh token을 검증하여 access token을 재발행 해주는 것으로 보안적으로 안전하게 유저 인증을 관리 할 수 있습니다.
✔ 프론트에서 안전하게 Token 관리하기
동작과정에서 살펴 보았듯이 client와 server 간에 인증 요청과 token응답을 주고 받아야 합니다. 이과정에서 token을 안전하게 관리하는 것이 매우 중요합니다.
프론트에서 보통 token을 저장하고 관리할 수 있는 곳은 local storage 와 cookie를 많이 사용합니다. (페이지 새로고침이나 브라우저를 재접속해도 로그인을 유지하기 위해)
하지만 두 방식 모두 XSS와 CSRF 공격에 취약할 수 있습니다.
∗ 브라우저 관련 보안 취약점
1) XSS(Cross-Site Scripting) 공격
- 공격자(해커)가 클라이언트 브라우저에 악의적인 스크립트를 삽입해 실행하는 공격이다.
- 보통 javascript같은 브라우저가 읽을 수 있는 코드를 사용하여 서버에서 스크립트를 실행하거나, url에 Javascript를 적어 클라이언트에서 스크립트 실행이 가능하다면 공격자가 사이트에 스크립트를 삽입해 XSS 공격을 할 수 있다.
- 사용자가 의도치 않은 명령을 실행 시키거나 쿠키, 세션 등 민감한 정보를 탈취할 수 있습니다.
2) CSRF(Cross Site Request Forgery) 공격
- 사이트간 위조 요청의 줄임말으로 공격자가 다른 사이트에서 우리 사이트의 API 콜을 요청해 실행하는 공격
- 사용자가 웹사이트에로그인한 상태에서 사이트간 요청 위조 공격 코드가 삽입된 페이지를 열면, 공격 대상이 되는 웹사이트는 위조된 공격 명령이 믿을 수 있는 사용자로부터 발송된 것으로 판단하게 되어 공격에 노출된다.
토큰 저장 방식으로만 모든 보안 취약점을 막는 것은 불가능 하지만 저장 위치에 따른 취약점을 인식하고 저장 위치를 결정하는 것이 중요합니다.
∗ front 에서 token 관리
1) local storage
- 브라우저 내의 local storage에 세션 id, refreshToken 또는 accessToken을 저장해두면 XSS 취약점을 통해 그 안에 담긴 값을 불러오거나, 불러온 값을 이용해 API 콜을 위조할 수 있습니다.
2) 쿠키 저장 방식
- 브라우저의 쿠키에 저장하는 방식으로 http 요청시 쿠키가 포함되어 서버에 전송하게 됩니다.
- 단, 쿠키는 자바스크립트로 접근이 가능하기 때문에 XSS 취약점을 통해 공격 받을 수 있습니다.
- CSRF 공격에도 취약
3) secure, httpOnly 쿠키 저장 방식
- 서버에서 쿠키를 설정할 때 httpOnly와 secure 옵션을 주어 방어하는 방법입니다.
- httpOnly 옵션의 경우 자바스크립트로 접근이 불가능 합니다.
- secure 옵션의 경우 https 접속에서만 동작
결론적으로, refresh token을 httpOnly Cookie로 저장해 CSRF 공격을 방어하고 access token은 header로 리턴 받아서 프론트에서 관리 하는 방법을 사용했습니다.
이를 통해 CSRF 취약점 공격 방어하고, XSS 취약점 공격으로 저장된 유저 정보 읽기는 막을 수 있습니다.
✔ 정리
- 사용자는 로그인 요청을 보내고 성공시 access token, refresh token을 응답 받습니다.
- 이때 보안을 위해 access token은 만료시간을 짧게 설정하여 리턴 하고, refresh token은 만료시간을 길게 설정하여 httpOnly Cookie로 응답 받습니다.
- 이후 인증된 유저임을 알리기위해 Authorization header에 access token 포함하여 요청을 보냅니다.
- access token 만료시 refresh token을 이용해 access token을 재발급 받습니다.
최근에는 웹을 사용하다 보면 카카오, 네이버 등 소셜로그인 기능을 볼 수 있습니다.소셜 로그인 역시 토큰 기반의 로그인 인증 방법이며 Oauth2.0이라고 하는 프로토콜로 유저 정보에 대한 리소스가 프로젝트에서 사용될 벡엔드의 DB에 있는 것이아니라 카카오, 네이버 등 이런 웹서비스에서 제공 받는 것 입니다.다음 글에서 Oauth2.0에서 다루어 보겠습니다.
참조
https://www.youtube.com/watch?v=y0xMXlOAfss
http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
https://velog.io/@syi9595/JWTJson-Web-Token%EC%9D%80-%EC%99%9C-%EC%82%AC%EC%9A%A9%EB%90%A0%EA%B9%8C
'CS' 카테고리의 다른 글
socket.io 딥다이브 - 개념 과 이해 (0) | 2023.06.29 |
---|---|
사용자 로그인 이해하기 (쿠키와 세션) (0) | 2022.04.25 |
SSR CSR 차이 (0) | 2022.04.08 |
jpg, png, svg 차이, 리액트에서 svg 다루기 (0) | 2022.01.25 |
댓글