들어가기에 앞서
Refresh Token을 Cookie로 처리할까 고민 하던 도중 막상 왜 그렇게 저장하는지 자세히 모르고 사용했던 것 같다.
그래서 Jwt의 문제점들을 파악한 후에 써야 후에 고생하지 않을 거 같아서 문제점들을 알아보기로 했다
JWT의 문제점들
XSS(Cross Site Scripting)
웹 해킹 중 하나인 XSS는 게시판이나 웹 등에 JS와 같은 스크립트 코드를 삽입 해 개발자가 생각하지 못한 기능이 작동하도록 하는 공격
대부분의 웹 해킹 공격 기법과는 다르게 사용자-> 클라이언트를 대상으로한 공격이다
ex) 해커가 원하는 링크를 클릭하면 사용자가 가지고 있는 Cookie등을 사용하고 있는 서버로 Request를 보낸다 그러면 서버는 Response로 응답하기 때문에 해커가 원하는 Cookie, Session등 안에 정보를 탈취할 수 있다
주요 목적은 정보 탈취이다
CSRF(Cross Site Request Forgery)
웹 어플리케이션 취약점 중 하나로 사용자-> 클라이언트가 자신의 의지와는 무관하게 공격자가 의도한 행위 (수정, 삭제, 등록 등)을 특정 웹사이트에 요청하게 만드는 공격
ex) 하나의 위조 사이트를 만든 뒤 비밀번호 변경, 인증 이메일 발송, 계좌이체 정보를 심어놓은 링크를 클릭하면 요청이 자동으로 해당 사이트에 전송됨 (해당 사용자가 로그인 했을 때 생긴 Cookie를 이용해서)
주요 목적은 정보 탈취보다는 서버에 대한 악성 요청이다
그렇다면 XSS와 CSRF의 차이는
XSS는 사용자의 정보를 탈취하는 것이 목적이라면 CSRF는 요청을 위조해서 사용자 몰래! 서버에 사용자가 한 것처럼 특정 요청을 보내는 것!
XSS 방어 방법으로는
Cookie에 Http Only 옵션을 설정해서 방어하는 방법이다
Http Only 옵션을 설정하면 Script에서 Cookie를 읽어올 수 없게한다 이렇게 해서 악의전인 Script에서 Cookie를 가져올 수 없기 때문에 XSS 공격에 방어가 된다
CSRF방어 법
위 XSS 방어 문제점은 아직 CSRF에는 아직 취약하다는 것 그렇다면 다른 방법으로는 JWT Token을 Cookie가 아닌 Header에 넣고 요청을 보내면 Cookie를 사용하지 않기 때문에 CSRF를 통한 공격에 방어할 수 있다 ... 그렇지만 Cookie를 Header에 넣어 보내면 HttpOnly 옵션을 해체해야 한다
검색을 해 봤을 때 보통 Cookie를 이용해서 Http Only로 설정하고 CSRF를 방어하는 형식이다 XSS는 일단 방어할 수 있기 때문에
CSRF방어를 하는 거 같다
그렇다면 Http Only로 하고 방어하는 방법은 무엇이 있을까?
CSRF Token
난수(CSRF Token)을 생성해 서버 메모리(Session)에 저장하고 사용자에게 전달한다
사용자는 중요한 요청을 보낼 때 파라미터로 CSRF Token을 같이 보내 검증을 한다
그렇다면 CSRF 공격을 당해도 CSRF Token은 서버에 전달되지 않아 서버는 요청을 수행하지 않는다.
Cookie Referer Check
요청을 보내면 요청을 보낸 Domain을 알 수 있는데 이 Domain이 내가 허용한 Domain에서 온 요청인지 체크하면 된다. 일반적으로 Referer Check로만 대부분의 CSRF를 방어할 수 있다.
CAPTCHA 도입
이건 좀 특이해서 가져와봤다 한 번씩 볼 수 있는 그림 맞추기인데 이렇게 사용하면 CAPTCHA인증코드가 없거나 틀리면 요청을 거부할 수 있도록 한다 이제 사이트마다 이런게 있는 이유를 이해했다
그리고 Spring Securiy에서는 공식적으로 CSRF 공격에 대한 방어 기능을 제공해주기 때문에 크게 걱정하지는 않아도 될 거 같다
음 정리하면 로컬 저장소에 저장하면 편리하긴 하지만 XSS공격에 탈취가 가능해서 좋은 선택지가 아닌 거 같다
그래서 AccessToken은 단기 휘발성 변수(local variable)로 저장하여 탈취될 수 없도록 하고
Refresh Token은 인증에 직접적으로 사용되는 토큰이 아니니 Http Only로 쿠키에 저장하자!
왜냐하면 CSRF 공격으로는 발급받은 AccessToken을 수령할 방법이 없기 때문이다 웹 사이트에서 대부분은 요청에 담긴 헤더를 검증한다 알지 못하는 클라이언트에를 대비하기 위해서다
(CSRF는 정상적인 URL로 요청을 하는 게 아니라 비정상적인 URL로 요청해 공격하는 방식이다) 중요!
따라서 CSRF 공격을 통해 이를 위회하려면 요청 보내는 클라이언트 주소(URL)을 위조할 수 밖에 없다 하지만 주소를 위조했으니 서버는 그 위조된 주소로 응답을 보낼 것이다
즉 실제로 요청을 보낸 공격자의 페이지로는 Response가 오지 않는다 그러면 Response에 담긴 AccessToken은 당연히 탈취할 수 없다 그래서 RefreshToken은 Http Only로 쿠키에 저장해야 안전하다!
--------------------------------------------------------------
다른 방법들?
글을 다시 보다가 추가할 것이 있어서 추가합니다.
RefreshToken은 Redis나 DB에 저장은 대중적으로는 이용하는 방법이다. RefreshToken을 탈취당할 경우도 생각한다면 Client가 전달한 RefreshToken의 값과 DB or Redis 휘발성 저장소가 아닌 곳에서 비교하여 맞는지 확인한다면 앞에서 말했 듯 탈취를 당해도 막아낼 수 있다.
그리고 RefreshToken과 Client가 접속해서 Request보낸 IP도 같이 검사를 한다면 보안성을 더 높일 수 있디.
예를들어 RefreshToken이 탈취 당했다면 IP가 다른 곳에서 탈취한 RefreshToken으로 보낼 것이다.
이럴때는 RefreshToken을 재발급하도록 로그아웃을 시키면 된다.
그리고 또 다른 내 주관적인 생각이지만 IP를 굳이 저장하지 않아도 될 것 같다.
IP로 RefreshToken을 암호화(인코딩, 해쉬 등)한다면 굳이 저장하지 않고 나중에 요청할 때 그 IP로 또 디코딩하면 되지 않을까?
근데 이생각에서 그렇다면 RefreshToken굳이? 라는 생각을 했는데...
만약 RefreshToken을 DB에 저장하지 않고 RefreshToekn값을 IP로 또 암호화 방식을 거친다면
둘다 저장하지 않고 Client가 보낸 RefreshToken + IP로 복호해서 사용자가 맞는지 확인한다면? 오히려 DB에 검증이 필요없지 않을까?(당연히 문제가 있어서 영구적인 저장소에 저장하겠지??)
이 고민은 시간이 나면 나중에 다뤄보도록 하겠다.
오류나 틀린 점 또는 질문은 댓글로 써주시면 감사하겠습니다.
참고한 글
https://ppaksang.tistory.com/16
https://medium.com/@uk960214/refresh-token-%EB%8F%84%EC%9E%85%EA%B8%B0-f12-dd79de9fb0f0
'스프링' 카테고리의 다른 글
[Spring] TDD vs BDD 무엇인지알고 비교하기 (0) | 2023.03.12 |
---|---|
[Spring] Slice를 이용하여 무한스크롤 구현하기 (0) | 2023.02.12 |
[Spring] Spring Security이용한 JWT 로그인 구현기 (1) | 2023.01.28 |
[Spring] 자바의 대표적인 빌드 관리 도구 Maven vs Gradle 차이 (0) | 2023.01.04 |
[Spring] Spring이란 무엇인가? (0) | 2023.01.03 |