인증 토큰, 세션을 가장 안전하게 저장하는 방법.



바야흐로 웹서비스, SAAS, 플랫폼 전성시대 입니다.

너도 나도 웹서비스를 만들고, 단순 정보성 웹페이지도 만들고, 만들 수 있는 것은 모두 만들내 내고 있습니다. 덕분에 개발자 몸값은 계속 올라가고 있고, 올라간 개발자 몸값 때문에 많이 점점 스타트업 장벽이 올라가는 느낌도 듭니다.


저는 회사에서 백엔드 개발을 주로 하고 있지만, Django 기반의 서비스의 오너십을 가지고 있기도 합니다. Django는 기본적으로 쿠키기반 세션 인증을 하지만, 메인서비스 들이 JWT 를 사용하고 있기 때문에 쿠키에 세션 대신 JWT를 넣어 두고 사용 하고 있습니다.


오랫동안 생각했지만, 그렇게 중요하지 않다고 생각해서 미뤄두었던 리서치를 해보았습니다. 웹개발을 한다고 해도 누구도 명확하게 한 번에 답변을 낼 수 없는 그 질문..

"그래서 JWT 토큰은 브라우저 어디에 저장해 두어야 하는가..?"


인터넷을 찾아보면 아주 아주 다양한 자신들만의 신념으로 또는 지식으로 또는 믿음으로 설명해 둔 글들이 있습니다. 구글이 서드파티 쿠키를 허용하지 않겠다고 말하면서 Trusted Token 이라는 저장 공간을 만들어 낸 것도 본인들이 "기준"을 만들어 내려고 하는 움직임인 듯 합니다.



1. localStorage 와 sessionStorage   


우선 로컬 스토리지와 세션 스토리지 차이는 분명합니다. 로컬 스토리지는 해당 도메인에 설정된 정보값을 브라우저 탭, 창 간에 공유하고 세션 스토리지는 공유하지 않습니다. 그래서 세션 스토리지에 저장해 버리면, 여러탭을 켜놓고 작업할 때 한 탭에서 로그아웃이 되어도 다른 탭에서는 로그아웃이 안될 수 있습니다. 뿐만 아니라.. 로컬 스토리지와 세션스토리지는 페이지 안에서 작동하는 자바스크립트로 얼마든지 엑세스 할 수 있습니다.

당신이 만든 페이지, 당신이 만든 자바스크립트... 해당 페이지에서 이것만 돌아갈까요?

이런 데이터는 구글 애널리틱스, 구글 애드센스 등 서드파티 자바스크립트 역시 엑세스 할 수 있습니다. 물론, 구글이나 페이스북 픽셀등이 이런 데이터에 접근하지는 않겠지만, 고객이 좋아하진 않겠죠? 설마.. 엑세스 해서 토큰들을 수집하고 있다면??? 우리는 알길이 없습니다. 또한, 크롬 등 브라우저 익스텐션도 접근할 수 있습니다. 때문에, 로컬 스토리지와 세션 스토리지는 사용하면 안됩니다.



2. 메모리    


그렇다면 메모리 안에 저장하는 것은 괜찮을까? 물론 로컬 스토리지와 세션 스토리지 보다는 훨씬 나을 수 있습니다. 글로벌로 접근만 할 수 있도록 하지 않는다면요. 글로벌로 접근이 가능하다면, 로컬 스토리지에 넣는 것과 별로 차이가 없습니다. 하지만, 이런 경우 엑세스 토큰 리프레시 토큰등을 새로운 브라우저 탭, 창을 열때마다 새로 발급받고 새로 관리하게 됩니다. 세션 스토리지와 마찬가지로, 한 탭은 로그아웃해도 다른 탭은 살아있는 이상한 UX를 고객에게 선사할 수 있습니다. 



3. 브라우저 쿠키    


프론트엔드 엔지니어라면, 가장 먼저 생각나는 것은 자바스크립트로 쿠키에 저장 하는 것 이겠지만, 자바스크립트로 브라우저 쿠키에 저장한다면, 이 쿠키도 로컬 스토리지와 마찬가지로 모든 다른 자바스크립트가 엑세스 할 수 있습니다. 뿐만 아니라 JWT를 사용한다면, 자바스크립트로 충분히 로그인된 사용자가 누구인지 알 수 있습니다. 굳이 Secret 키 가 없어도 안에 내용은 다 decode 가 가능하기 때문입니다. 만약 악성 코드가 돌고 있다면, 당신의 고객의 토큰은 이미 탈취 되었습니다.


아.. 그럼 무엇을 이용해야 할까?



   HTTP 쿠키를 사용하세요.


토큰은 서버측에서 생성한 쿠키에 저장할 수 있습니다. 쿠키는 "반드시" HttpOnly 라는 속성값을 부여하여 저장해야 합니다. 이 속성을 주면 자바스크립트가 이 쿠키를 읽을 수 없게 할 수 있습니다. 이 말은 크로스 사이트 스크립팅(XSS) 역시 막을 수 있습니다.

쿠키는 Secure 라는 속성도 있습니다. 이 쿠키는 HTTPS 통신에서만 사용할 수 있도록 설정하는 것입니다. 이렇게 사용하면 "중간자 공격" (Man-in-the-middle Attack)으로 부터도 보호받을 수 있습니다. 중간자 공격은 통신 중간에 정보를 도청/위조 하는 공격입니다.

여기서 다시 SameSite 라는 속성에  Strict나 Lax 속성을 준다면 오직 서버만이 토큰을 저장할 수 있게 합니다. 이렇게 하면, "크로스 사이트 요청 위변조 공격" 에서 자유로울 수 있습니다. Django는 Cross-site Request Forgery 공격을 막기위해 CSRF 토큰을 추가로 부여하는데, CSRF를 사용하지 않을 예정이라면 SameSite 속성을 변경하는 것도 좋은 방법입니다.

여기서 또 다시 Domain 속성을 준다면 해당 쿠키는 지정된 도메인 서버로만 가는 요청에만 사용됩니다. 


아래는 Dingrr의 쿠키 사용 내역 입니다.

JWT와 같이 인증을 위해 사용되는 Sessionid는 아래 옵션이 설정되어 있습니다.

  • Domain : dingrr.com
  • SameSite: LAX
  • HttpOnly : True
  • Secure : False 하지만, Dingrr는 HTTP접속이 되지 않습니다.


Secure를 True로 할 경우 로컬에서 개발할때 어려움이 있을 수 있습니다. 하지만, 이것도 충분히 극복가능하니 Secure 속성을 사용하는 것도 고려해 볼만 합니다.






  • [[a.original_name]] ([[a.file_size | fileSizer]])
좋아요[[ postLike | likePlus ]]
공유
라이언

“Lead Python Engineer”

댓글 [[totalCommentCount]]
[[ comment.author__nick_name ]] [[ comment.datetime_updated | formatDate]] (수정됨)

[블라인드 처리된 글 입니다.]

답장
[[ sub.author__nick_name ]] [[ sub.datetime_created | formatDate ]] (수정됨)

취소
댓글을 남겨주세요.
'데브옵스' 관련 최신 포스트
[[ post.title ]]
[[ post.datetime_published_from | DateOnly ]]