HTTP 기본 인증 방식이 모바일에서는 raw한 api키를 매 요청 때마다 사용해야 하는 문제가 있어 취약점 공격에 노출될 가능성이 높다. 또한 API 키가 내장된 채로 배포하면 리버싱을 통해 API 키를 알아내는 것이 가능하기에 서비스가 악용될 소지가 있다. 그렇기에 HTTP 기본 인증 방식은 웹 브라우저나 모바일 앱과 같은 신뢰하기 힘든 환경에 적합한 인증 방식은 아니다.
엑세스 토큰은 JWT(JSON Web Token)를 주로 사용하는데, JWT는 다음과 같은 특징을 갖는다.
토큰 구조는 Header, Payload, Signature로 나눠진다.
Header, Payload, Signature 정보를 암호화 할 방식(alg), 타입(typ) 등이 들어간다.
암호화 할 방식(alg)에는 보통 HMAC SHA256
또는 RSA
가 보통 사용되며, 타입(typ)에는 토큰의 타입을 지정하면 된다.
{
"typ": "JWT",
"alg": "HS256" // HMAC SHA256
}
토큰에 담을 정보가 들어간다. 일반적으로 사용자의 ID, 유효 기간이 포함되어 있다.
정보의 한 '조각'을 클레임(claim)이라고 부르고, name-value의 한 쌍으로 이뤄져 있다. 토큰에는 여러 개의 클레임들을 넣을 수 있는데 클레임의 종류는 다음과 같다.
클라이언트에 대한 정보, META data 같은 내용이 들어있으며, Base64로 인코딩 되어있다.
Base64 방식으로 인코딩 한 Header, payload 그리고 Secret key를 합친 후에 토큰이 생성된다.
로그인 구현을 할 때 JWT로 구현하면서 궁금한 점이 있어서 글을 적어본다.
내가 로그인 구현을 할 때 로직은 아래와 같다.
문제는 Access Token의 만료기간을 길게 잡으면 공격자가 탈취해 유저의 정보를 쉽게 얻을 수 있다는 점이 있다.
만료기간을 짧게 한 후, 만료되었을 때 서버에서 토큰을 재발급 해줘야 하는데 검색하다가 찾은 것이 Refresh Token이다.
일반적으로 Access Token의 만료기간이 만료되면 사용자는 Access Token을 얻기 위해 다시 인증을 해야 한다. Refresh Token 을 이용하면 재 인증을 건너 뛸 수 있으며, API에 대한 요청으로 클라이언트가 애플리케이션 Resource에 계속 액세스 할 수 있는 새로운 Access Token을 얻을 수 있다.
그렇기에 만료기간이 길고, 사용횟수가 적기에 보관이 중요하다.
Refresh Token을 담고 있는 저장소에서 클라이언트가 보낸 토큰이 동일한 클라이언트가 맞는지 확인하는 것이다. 동일한 클라이언트일 경우 새로운 토큰을 생성하고 반환하여 엑세스 토큰을 보낸다.
결국에 Refresh Token으로 Access Token을 발급하는데 있어, 공격자가 Refresh Token를 탈취되면 Access Token을 발급 받을 수 있고, 결국 공격자는 클라이언트로 위장해 정보를 얻어 갈 수 있다는 점이 있다.
그러면 Refresh Token이 왜 필요한지에 대한 의문점이 들었다.
물론 Access Token과 Refresh Token이 있을 경우 Access Token이 300번 요청 될 때, Refresh Token은 1번 요청 될 수도 있어서 나름 보안적이긴 하나.. Refresh Token 이 중간에 탈취 당했을 경우 어떻게 대처해야 좋을지 아직은 모르겠다.
결론적으로는 완벽한 보안은 없다. 고민을 하면서 차선책으로 선택을 해야하는데 유저 편의성을 중요하게 생각하는 프로젝트냐, 보안이 좀 더 중요한 프로젝트냐에 따라서 다를 것 같다. 금융 프로젝트인 경우 보안을 조금 더 중요하게 생각하기에 수시로 로그인을 받으면 되고, 커뮤니티 프로젝트인 경우에는 편의성을 고려해 로그인을 유지시키는 게 좋을 듯 하다.