로그인 인증 방법
1. Basic 인증
모든 HTTP 요청에 아이디와 비밀번호를 같이 보내서 인증하는 방식이다.
최초 로그인한 후 HTTP 요청 헤더의 Authorization 부분에 Basic <ID>:<Password>
처럼 아이디와 비밀번호를 콜론으로 이어붙인 후 Base64로 인코딩한 문자열을 함께 보낸다.
이 요청을 수신한 서버는 인코딩된 문자열을 디코딩해 아이디와 비밀번호를 찾아낸 후 사용자 정보가 저장된 데이터베이스 또는 인증 서버의 레코드와 비교한다. 비교결과 아이디와 비밀번호가 일치하면 요청받은 일을 수행, 아니면 거부한다.
요청 헤더 예시
Authorization: Basic aGvsbG93b3JsZeBnbWFpbC5jb206MTIzNA==
단점
- 아이디와 비밀번호를 노출하게 된다.
- 인코딩은 보안을 목적으로 하는게 아니다. 디코더만 있다며 누구나 아이디와 비밀번호를 확인할 수 있다.
- 누군가 HTTP 요청을 가로채는 것을 MITM(Man in the Middle Attack) 이라 한다.
- 따라서 반드시 HTTPS와 사용해야 한다.
- 사용자를 로그아웃시킬 수 없다.
- 모든 요청이 일종의 로그인 요청이기 때문이다.
- 사용자의 계정 정보가 있는 저장 장소(인증 서버나 DB)에 과부하가 걸릴 확률이 높다.
- 1초에 10만 개를 처리하는 서버가 있다 가정하자.
- 10만 개의 요청을 확인하려면 10만 번 계정 정보의 저장 장소를 갔다 와야한다.
2. 토큰 기반 인증
- 토큰(Token)은 사용자를 구별할 수 있는 문자열이다. 대표적으로 Bearer Token이 있다.
- 토큰은 최초 로그인 시 서버가 만들어 준다. 서버가 자신만의 노하우로 토큰을 만들어 반환하면 클라이언트는 이후 요청에 아이디와 비밀번호 대신 토큰을 계속 넘겨 자신이 인증된 사용자임을 알리는 것이다.
- 사실 토큰은 세션 기반 인증과 별 다른게 없다. 이름만 바뀐 세션일 뿐이다.
- 토큰 이용만으로는 스케일 문제를 해결할 수 없다.
3. JSON 웹 토큰
- 전자 서명(Digital Signature) 된 토큰을 이용해 스케일 문제를 해결할 수 있다.
- JWT도 토큰 기반 인증이므로 서버가 생성하지만, 전자 서명을 한다는 점이 다르다.
- JWT 토큰은 {header}.{payload},{signature}로 구성돼 있다.
- JWT의 예는 다음과 같다. :
Authorization: Bearer eyKhbGci0iJIUzUxMiJ9.eyasdkfajsdlkfasdfklja.sdflaskdjf;lasdfjkalsdf
- 인코딩된 토큰을 Base64로 디코딩하면 아래와 같다.
{ // header
"typ": "JWT",
"alg"L "HS512"
},
{ // payload
"sub":"4028809378739d3891839a89481883c13",
"iss": "demo app",
"iat": 1595733657,
"exp": 1596597657
}. //signature
Nn4dasdfasdjhfsdfasdfjD3fDFHHeufsiefh3f98742fJHFKEH13usdhfw
각 파트의 필드
- Header
- typ: Type을 줄인 말로 토큰의 타입을 의미한다.
- alg : Algorithm을 줄인 말로 토큰의 서명을 발행하는 데 사용된 해시 알고리즘의 종류를 의미한다.
- Payload
- sub: Subject를 줄인 말로 토큰의 주인을 의미한다. sub는 ID 처럼 유일한 식별자여야 한다.
- iss: Issuer를 줄인 말로 토큰을 발행한 주체를 의미한다.
- iat: issued at을 줄인 말로 토큰이 발행된 날짜와 시간을 의미한다.
- exp: expiration을 줄인 말로 토큰이 만료되는 시간을 의미한다.
- Signature
- 토큰을 발행한 주체 Issuer가 발행한 서명으로 토큰의 유효성 검사에 사용된다.
전자 서명
- JWT에서 전자 서명이란 {헤더}.{페이로드}와 시크릿키를 이용해 해시 함수에 돌린 암호화한 결과 값이다.
- 시크릿키는 나만 알고 있는 문자열, 비밀번호 같은 것이다. 너무 간단하지만 않으면 아무거나 상관없다.
로그인 과정
- 서버는 사용자의 아이디와 비밀번호를 서버에 저장된 아이디와 비밀번호에 비교화 인증한다.
- 만약 인증된 사용자인 경우 사용자의 정보를 이용해 {헤더}.{페이로드} 부분을 작성하고 자신의 시크릿키로 {헤더}.{페이로드} 부분을 전자 서명한다.
- 전자 서명의 결과로 나온 값을 {헤더}.{페이로드}.{서명}으로 이어붙이고 Base64로 인코딩한 후 반환한다.
인증 과정
- 누군가 이 토큰으로 리소스 접근을 요청한다.
- 서버는 이 토큰을 Base64로 디코딩 후 얻은 JSON을 {헤더}.{페이로드}와 {서명} 부분으로 나눈다.
- 서버는 {헤더}.{페이로드}와 자신이 갖고 있는 Secret 으로 전자 서명을 만든 후 방금 만든 전자 서명과 요청으로 들어온 {서명}부분을 비교해 유효성을 검사한다.
단점
누군가 토큰을 훔쳐가면 어떻게 될까? 해당 계정의 리소스에 접근할 수 있게 된다. 그렇기에 반드시 HTTPS를 통해 통신해야 한다.
Reference
React.js, 스프링부트, AWS로 배우는 웹 개발 101 (에이콘, 김다정)