브라우저에서 JWT를 저장할 수 있는 곳은 로컬스토리지, 쿠키 두군데가 있다.
하지만 로컬스토리지나 쿠키나 모두 자바스크립트로 읽을 수 있는 값들이기 때문에
아무런 처리없이 이곳에 저장하면 굉장히 보안에 취약하다.
해커가 이미지 태그 혹은 브라우저 URL에 자바스크립트를 삽입 할 수 있는 취약점이 있다면
이렇게 로컬스토리지나 쿠키에 저장된 토큰은 바로 허술하게 털려버린다.
브라우저의 로컬스토리지는 정말 편리하지만 XSS를 방어할 수 있는 방법이 없기 때문에 로컬스토리지보다는 쿠키가 그나마 더 보안에 낫다. 단, 이 쿠키카 Http Only이며 Secure(https)옵션이 켜져있을때 그렇다. 이 말은 쿠키를 자바스크립트로 읽어올 수 없다는 말이고 네트워크 감청을 통한 쿠키값 읽기를 방어한다는 말이다. 즉, XSS 공격을 통한 토큰 탈취를 방지할 수 있다.
공격자가 상대방의 브라우저에 스크립트가 실행되도록 해 사용자의 세션을 가로채거나, 웹사이트를 변조하거나, 악의적 콘텐츠를 삽입하거나, 피싱 공격을 진행하는 것을 말합니다
-> 권한이 없는 사용자가 악의적인 용도로 웹 사이트에 스크립트를 삽입하는 공격 기법
그럼 Http Only Secure 쿠키에 액세스토큰과 리프레시 토큰을 둘다 저장해야 하는가? 라고 질문하면 그것도 그렇지 않다.
왜냐면, 쿠키라는것이 브라우저가 서버에 요청을 보낼떄 자동으로 포함해서 보내는 값이기 때문이다. 만약에 그 쿠키값안에 세션ID나 엑세스토큰이 들어있다면 해커가 내 인증정보를 활용해서 서버에 나인것처럼 속여서 요청을 보낼 수 있게 된다. 이게 CSRF공격이다.
보안은 시나리오를 생각해보면 이해가 빠른 것 같다. 내 사이트에 관리자로 로그인한 상태로(쿠키에 세션ID 혹은 엑세스토큰이 담겨져있다고 해보자.) 네이버 메일을 열었는데, 해커가 보낸 메일 속 이미지에 내 사이트 서버에 어떤 A 유저의 정보를 삭제하는 API 주소가 적혀있다고 해보자.
나는 그 유저 정보 삭제 API를 호출하고 싶지 않았는데 브라우저는 이미지 태그 안에 있는 스크립트를 자동으로 실행하기 때문에 갑자기 A유저 정보가 삭제되는 대참사가 발생한다. (내 브라우저 쿠키에는 인증 정보를 갖고 있기 때문에.. 브라우저 입장에서는 해커가 그 요청을 보낸게 아니라 내가 보낸게 되어버림..) 이게 바로 Cross Site Resource Forgery(교차 사이트 요청 위조 공격)이다.
그러니까 이 문제를 방지하려면 항상 서버는 요청이 왔을때 그 요청이 진짜 내 사이트에서 왔는지 확인을 해야한다.
그런 방법으로는
Referer 체크 (요청이 어디서 왔는가?)
CSRF 토큰 (난수 생성해서 서버에서 확인하는 방법)
두가지가 있다. 아무튼 여기까지가 CSRF에 대한 설명이다.
쿠키라는것이 결국에 브라우저에서 자동으로 보내는값이기 때문에 인증에 활용하기도 하지만 그것때문에 CSRF 보안에 취약하다. 그래서 결론은 리프레시 토큰만 http only secure 쿠키에 저장하고 액세스 토큰은 프로그램상 로컬변수에 담아 놓는것이 그나마 제일 안전하다.
이렇게 되면,
1. 해커가 CSRF 공격을 하더라도 쿠키에는 액세스토큰이 없기 때문에 인증 불가 상태가 되어 공격이 차단된다.
2. 해커는 http only secure 쿠키 특성상 리프레시 토큰 자체를 털 방법이 없다.
XSS는 완벽하게 막을 수 없는데 그 이유는 어찌됬건 내가 인증을 한 상태에서는 서버에서 나에게 허용된 어떤 API들이 있는데 그 API들은 마찬가지로 XSS를 통해 해커도 호출할 수 있다는 얘기기 때문이다.
어쨌든 액세스토큰은 지속적으로 네트워크를 타며 노출될 가능성이 높기 때문에 만료시간을 짧게 가져가야 한다.
그리고 쿠키에 저장된 리프레시토큰을 가지고 매번 액세스 토큰을 갱신해가면서 사용하도록 로직을 구성해야한다.
마지막으로, 사용성을 위해 액세스 토큰이 HTTP 헤더에 없거나 만료된 경우 리프레시 토큰으로 조용히 토큰 재발급 과정이 일어날 수 있도록 로직을 작성해야한다. (silent token refresh)