반응형

스터디 도중 httpOnly 쿠키가 등장했는데 이해가 안되는 부분이 있어서 하루 동안 구글링하며 알아봤다.

용어 정리

토큰

서버에서 사용자를 구분하는 데이터. HTTP는 상태를 가지고 있지 않으므로 토큰 같은 게 없다면 사용자를 구분할 수 없다. 요청에 토큰을 담아 보내면 서버가 사용자를 판단하고 작업을 수행한다.

개인정보나 다름없으므로 관리에 신경 써야 한다.

XSS (Cross-site Scripting)

공격자가 사이트에 스크립트를 주입하는 공격. 사용자가 해당 사이트에 접속하면 스크립트가 실행된다. 자바스크립트 코드를 실행하므로 자바스크립트로 할 수 있는 모든 것을 할 수 있음

흔한 사례로 XSS 방지 처리가 되지 않은 게시판에 <script> 태그를 넣은 글을 작성하면 해당 글을 읽은 사람들에게 자바스크립트 코드가 실행됨

CSRF (Cross-Site Request Forgery)

사용자가 의도하지 않은 요청을 보내게 하는 공격. 공격자는 사용자인 척하고 API 요청을 보낼 수 있음

흔한 사례로 <img> 태그에 로그아웃 주소를 넣은 글을 작성하면 해당 글을 읽은 사람들은 로그아웃 요청을 보내게 되고 로그아웃이 된다. (img 태그는 이미지를 불러올 때 GET 메소드를 사용)

토큰을 저장할 수 있는 곳

var, 리덕스 스토어 등 자바스크립트 변수

변수는 휘발성이다. 페이지를 새로고침하면 토큰이 날아가므로 매번 새로 로그인을 해야 한다. SPA라면 페이지 전환 시 새로고침이 되지 않으므로 상황이 낫지만 새로고침하면 다시 로그인해야 하는 건 마찬가지

다만 통째로 가져갈 수 있는 쿠키, 로컬/세션 스토리지와는 다르게 어디에 저장됐는지 공격자가 분석해야 한다는 점에서 장점이 있다.(특히 로컬 변수라면 더더욱! 물론 개발자도 고려해야 할 부분이 많아짐)

어쨌든 고려할 가치 없음

쿠키

자바스크립트로 접근이 가능하므로 XSS에 취약하다. 쿠키는 요청을 보낼 때 자동으로 전송되므로 CSRF에 취약하다.

XSS의 경우 httpOnly 옵션을 사용하면(서버에서 지정) 자바스크립트에서 접근이 불가능해지므로 공격을 차단할 수 있다. 다만 개발자도 접근할 수 없으므로 토큰 전송, 로그인 여부 판단 등의 로직을 짤 때 다른 접근법을 사용해야 한다.

CSRF의 경우 SameSite 등의 옵션으로 완화할 수 있다. 하지만 쿠키는 항상 자동으로 전송되기 때문에 개발자가 제어할 수 없으므로 완전한 차단은 불가능하다.

로컬 스토리지

만료일 지정이 가능한 쿠키와는 다르게 영구 저장이기 때문에 자동 삭제 등을 구현하려면 귀찮은 부분이 있음. 그래도 별도 라이브러리를 사용하지 않을 때 쿠키보다 다루기가 편하다.

httpOnly 옵션을 사용하지 않은 쿠키와 마찬가지로 자바스크립트로 접근이 가능하므로 XSS에 취약하다.

자동으로 전송되지 않기 때문에 CSRF에는 해당 사항이 없다.

세션 스토리지

로컬 스토리지와 유사하나 데이터가 세션별로 유지된다는 점이 다르다. (창을 닫으면 데이터가 사라짐) 자바스크립트 변수와는 다르게 새로고침 시에도 데이터가 유지되지만 새 탭/창을 열면 새로운 세션이 생성되므로 새로 로그인 해야 한다.

탭별로 다르게 로그인하는 걸 원하지 않는 이상 고려할 가치 없음

결론

세션ID 하나만 저장하는 방식의 경우 httpOnly 쿠키를 사용하는 게 베스트이다. 물론, httpOnly 설정은 서버에서 하므로 서버와의 조율이 필요하다.

그리고 토큰 관리를 프론트에서 할 수 없으므로 토큰 저장 여부로 로그인 여부를 판단하는 로직은 사용할 수 없다. 아무래도 서버로 요청을 보내고 응답에 따라 로그인 여부를 판단하는 식으로 만들어야 할 듯

그리고 앞서 말했다시피 CSRF에 취약하다.

accessToken, refreshToken 이렇게 두 개를 사용하는 경우 refreshToken은 httpOnly 쿠키에 accessToken은 자바스크립트 변수에 저장하는 방법이 가장 베스트다. accessToken은 개발자가 직접 접근할 수 있으므로 언제 어떻게 담아 보낼지 제어가 가능해진다.

XSS 공격 시 refreshToken은 httpOnly 쿠키이기 때문에 얻을 수 없고 accessToken은 어디에 있는지 몰라서 얻기가 까다롭다. CSRF 공격을 당하더래도 같이 전송되는 refreshToken만으로는 할 수 있는 게 없다.

만약 로컬 변수에 accessToken을 저장하기 곤란한 상황이라면 결국 쿠키와 로컬 스토리지 중 선택해야 하는데 이 부분은 의견이 분분하지만 나는 두 가지 모두 XSS에 취약한 건 마찬가지니 사용하기 편한 비교적 최신 기능인 로컬 스토리지에 저장하는 게 낫다고 생각한다.

반응형

+ Recent posts