Published
- 13 min read
Digest 인증과 사용자 경험 개선
제품의 배경
우리 제품은 애초에 웹 사업을 염두해두고 출발한 제품이 아니다. IP Camera 혹은 NVR, DVR과의 통신을 염두하기 때문에 통신 방식의 주류는 ONVIF와 RTCP/RTSP이며 HTTP 통신은 마이너다. IP 카메라들이 HTTP 통신을 하지만, 시장에 나와있는 (내가 봤던)모든 제품들은 Digest 인증 방식을 사용한다. 우리 VMS의 경우 브라우저를 통해서 쓰는게 아니라 Windows APP위에 CEF를 올려서 쓰고 있다. 많은 API들은 여전히 커스텀 프로토콜을 이용해서 네트워크를 타지 않고 자사 클라이언트와 통신한다. 당연히 Digest 인증을 사용한다.
문제 파악
우리 보스가 자사 클라이언트의 의존성을 낮추고 싶다고 말했다.
그러다가 올해 드디어 웹 사업 중 브라우저를 통해서 우리 제품을 사용하는 유즈케이스가 생겼고 다음과 같은 상황이 발생했다.
몇 분 지나면 계속 브라우저에서 로그인 창이 뜹니다.
우리가 Digest 인증을 사용하고 있기 때문에 발생한 문제이다.
Digest 인증 간단 설명
다이제스트 인증도 웹 표준이다. 기본적으로 다이제스트 인증 방식은 CHF 이용하여 Digest를 생성하고 평문 전송을 방지하는 방식이다. 통신의 과정은 다음과 같다.
- 클라이언트가 서버에 요청을 보낸다.
- 서버는 401 Unauthorized를 보내되, 헤더에 realm, nonce, algorithm, qop를 포함한다.
- 클라이언트는 위 값을 기반으로 cnonce와 digest를 계산하고 이를 포함하여 다시 요청을 보낸다.
- 서버는 digest 검증 후 repauth를 포함한 응답을 보내준다.
DB를 사용하지 않음
위 인증 방식에서 서버 측은 해시값을 계산한 후 해당 값을 파일로 저장하게 된다. 그리고 파일 접근은 네트워크를 타지 않기 때문에 DB 사용보다 상대적으로 빠르다.
stateless
nonce가 중요한데, nonce는 reply attack을 방지하기 위해 서버가 매 요청마다 생성하는 값이다. 기본적으로 Digest 인증은 stateless이고 매 요청마다 위 과정을 반복해야 한다.
사용자 경험
현대 브라우저는 Network로 들어오는 모든 요청을 까뒤빈다. 그러다가 Authorization 헤더, 특히 Digest 인증에 사용되는 헤더를 만나면 UI 스레드를 블로킹한다. Digest 인증은 매 요청마다 새로운 Nonce를 생성하기 때문에 아래와 같은 상황이 발생한다.
- 사용자가 특정 버튼을 눌렀는데 이게 서버측에 요청을 총 3번 보낸다.
- 브라우저는 UI 스레드 블로킹하고 사용자에게 username, password를 받는다.
- 요청 성공했으나 2번째 요청은 이미 비동기적으로 날라갔기 때문에 컴포넌트 재랜더링 이후 2번째 응답이 도착하면
- 브라우저는 UI 스레드 블로킹하고 사용자에게 username, password를 받는다.
- 요청 성공했으나…(그긴거)
- (그긴거)
사용자 경험이 끔찍하다. 특히, WebAPP 기동시 처음 받는 요청때문에 초기로딩 점수가 낮다. UI 스레드를 블로킹하면 사용자는 화면에서 볼 수 있는게 아무것도 없고 로그인만 3번정도 한다.
세션을 사용하지 않고 JWT 토큰 방식을 사용한 이유
물론 우리가 기본으로 DB를 안쓰기는 한데 사용자에게 DB를 구축해달라고 고지를 하면 사용자 측에서 DB를 구축하고 우리 제품에 연결하는 식으로 DB를 쓸 수는 있다. 그래서 세션방식과 JWT 토큰 인증방식에서 DB는 문제가 되지 않았다.
그러면 둘 중 무슨 방식을 사용할 건지에 대해 의사 결정이 필요한데, 세션과 JWT 토큰 방식 둘 다 장단점이 있다. 세션은 모든 요청에 대해 DB를 타기 때문에 성능이 떨어지고 JWT는 토큰의 저장 주체가 클라이언트다 보니 보안적인 우려가 있고 등등…
우리도 저런 장단점을 바탕으로 의사결정을 하고 싶지만, 사실 저런 이유는 아니고 현실적인 문제때문에 JWT를 썼다. 일단, 우리 제품이 ‘웹’이라는 플랫폼을 고려하고 만든 제품이 아니다 보니 자사 프레임워크 기반에서 세션을 구현하기가 어려운 것으로 보였다.
뭐 어쨌건 나는 백엔드 개발자가 아니기 때문에 두 방식의 장단점에 대한 정보만 넘기고 최종 결정은 백엔드 개발자가 내렸다. 결과는 JWT 토큰 방식을 사용하는 것이다.
왜 Authorization 헤더를 썼는지
우리 Access Token은 Authorization 헤더에 담아서 보낸다. 왜 그렇게 하냐면…
- 타 시스템 고려
Authorization 헤더는 HTTP 표준이고 다른 시스템(대표적으로 내가 개발하는 프록시 서버)에서 인증 로직을 처리할 때 일관성을 유지할 수 있다.
- 브라우저 의존성 제거
데스크톱 앱이나 모바일은 브라우저처럼 쿠키라는 저장공간이 존재하지 않는다.
Public Certificate를 발급받자고 주장한 이유
비용 문제와 사업의 특성 등 현실적인 이유들로 받아들여지지 않았으나 기본적인 골자는 중간자 공격 우려였다.
왜 쿠키를 서버 측에서 만들어달라고 주장했는지
백엔드 측 구현이 힘들기 때문에 넘어갔지만 내 주장의 원인은 다음과 같다.
local storage에 저장하면 JS로 접근이 가능하기 때문에 XSS나 CSRF 공격에 취약하다.
쿠키 파일을 단말기에서 빼내는 방식
우리 제품의 유스 케이스 자체가 하나의 단말기에 여러 공장 노동자들이 붙어서 사용한다. 그러다보니, 나쁜 마음을 먹은 노동자가 USB 들고 가서 직접 쿠키 파일 담아간 다음, 그 쿠키파일에 저장된 Refresh Token을 탈취하면 어쩌냐는 질문을 했을 때 나는 못막는다고 했다(그리고 속으로 대단하다고 생각했다). XSS나 CSRF 논점에서 벗어난 이야기 아닌가? 라는 생각을 하기는 했다.
조작된 브라우저 사용
조작된 브라우저 사용이라니, 그게 자주 일어나나?라고 생각할 수 있겠지만 애초에 우리 제품부터가 Chromium 기반으로 직접 만든, 거기다 인증과 관련하여 자동화된 인증이 백그라운드에서 발생하는 조작된 브라우저를 사용하고 있었다. 그러다보니 이런 상황에 대한 고려를 한 것으로 추측한다. 아까 상황과 마찬가지이긴 한데 조작된 브라우저 사용은 논점과 어쩌구…
왜 Access Token과 Refresh Token을 분리하자고 했는지
이건 그냥 백엔드 개발자 분이 다른 일로 바빠서 못했는데 분리하자고 했던 원인은 다음과 같다.
- JWT 할아버지가 와도 토큰 탈취는 못막는다.
토큰 탈취는 당할 수 있다라고 생각하고 피해를 줄이는 접근이 옳다. 우리 제품은 아주 긴 기간의 토큰 만료 기간을 가지고 있었다. 토큰 탈취될 경우, 공격자가 그 토큰을 가지고 무언가 할 수 있는 시간이 너무 길다.
그렇다고 짧게 잡자니 기존의 문제점을 해결하지 못한다(사용자 경험). 그러면 토큰을 2개 쓰면 된다. 이에 대한 글은 다른 곳에도 많으니 생략.
‘Access Token을 탈취당했다면 Refresh Token도 동시간에 탈취당한 상황인데 왜 분리하나요’라는 물음도 있었는데 아래에 설명할 2가지가 관련있다.
모든 요청에 Access Token과 Refresh Token을 함께 담아서 보내지 않는다.
물리적 탈취를 제외하면 결국 Token 탈취는 네트워크를 타는 요청에 대한 탈취다(프록시, 중간자 공격 등등).
access token은 어쩔 수 없는데 refresh token은 access token을 재발급하는 API를 호출할 때에만 보내도록 했을 때 상대적으로 탈취의 빈도가 낮아진다. 적어도 Access Token이 탈취당한 그 시점에 Refresh Token까지 같이 탈취당하는 경우는 발생하지 않는다.
Refresh Token Rotation
다른 개발자분이 Refresh Token에 의구심을 가진 이유 중 가장 큰게 Refresh Token 탈취당하면 Access Token 무한 자판기랑 같은 건데 이거 어떻게 할거냐였다.
일단 검색했을 때 가장 많이 언급되는 방식은 Refresh Token을 재발급하는 API를 호출할 때마다 기존의 Refresh Token을 무효화하는 방식이다. 그러면 탈취당한 Refresh Token은 Access Token을 재발급하는 과정에서 오류가 나게 된다. 처음에 Access token 발급받는 경우는 어떻게 하냐고? 아까도 말했지만 탈취당한다고 생각하고 피해를 줄이는 측면에서 바라보아야 한다.
얼마나 감내해야 하는가
토큰을 클라이언트에 맡기는 방식을 채택한 이상 보안상의 Trade-off는 감내해야 한다.