0. 글 작성 배경
- Refresh Token을 어디에 저장하는 것이 좋은지 찾다보니 관련된 보안 관련된 내용이 많아서 정리하게 되었습니다.
- XSS, CSRF공격이 어떤 공격인지 와 방어책, CORS정책에 대해서 알 필요가 있기 때문에 이에 대해서 소개합니다.
- Backend에서 어떻게 해당 공격들을 방지해야 할지 정리하였습니다.
JWT Token
JWT 인증방식에서 Refresh Token을 왜 사용할까?
- JWT의 장점은 Session과 다리 인증 기능을 위해 서버에 정보를 유지하지 않고 Stateless 하게 구성 할 수 있다는 점입니다.
- 하지만 이 때문에 Token을 일단 발급하고 나면 원하는 때에 만료시키는 등의의 인증 상태에 대한 조작이 Session에 비해서 힘들다는 단점이 있습니다.
- Refresh Token발급을 통해 이러한 단점을 보완하면서도 보안성을 유지할 수 있습니다.
Refresh Token은 어떻게 구현될까?
- 로그인 시 Access Token과 함께 Refresh Token을 발급하고 Access Token의 경우 만료시간을 분단위로 짧게 유지시킵니다.
- Refresh Token의 경우 만료시간을 비교적 길게 두어 Access Token만료 시 Refresh Token으로 재 발급 하는 방식으로 구현합니다..
- 혹시나 Access Token이 탈취당하더라도 만료 시간이 적기 때문에 토큰을 탈취 당했을 때 위험이 감소합니다.
- 특정 유저를 로그아웃 시키고 싶을 경우 즉시 로그아웃 시킬 수는 없지만, 만료시간 이후 Refresh Token으로 Access Token을 재발급 할 때에 User정보를 조회하고 재발급이 가능한 유저인지 확인하는 로직을 두는 식으로 구현할 수 있습니다.
Refresh Token 저장 위치?
- Backend DB, Redis 등의 Storage에 저장하거나 Client측에 저장할 수 있습니다.
- Backend 측에 저장할 경우 JWT를 사용하는 이유인 서버를 Stateless하게 유지하려는 의도와 충돌하기 때문에 특별한 경우가 아니면 Client측에 저장하는 것이 일반적입니다.
- Client측에서는 Local Storage 혹은 Cookie에 저장할 수 있습니다.
Local Storage에 저장하는 경우
Cookie에 저장하는 경우
- 장점
- HttpOnly Flag로 XSS 공격을 방지할 수 있습니다.
- 단점
1. XSS, CSRF 공격.
1.1 XSS 공격이란
- XSS 공격은 웹사이트 관리자가 아닌 이가 웹페이지에 악성 스크립트를 삽입할 수 있는 취약점입니다.
- Script 코드를 해커가 삽입 시켜 동작하게 만드는 공격입니다.
- 이를 방지할 수 있는 방법은 기본적으로 입력, 출력값에 대한 검증이며 자세한 내용은 다음 링크를 참조해 주세요.
1.2 CSRF(**Cross-Site Request Forgery)** 공격이란
- 사이트간 요청 위조, 사용자가 자신의 의지와 무관한 요청을 만들게 만드는 공격.
1.2.1 전제 조건과 공격 과정
CSRF 공격을 위한 조건과 과정에 대해 알아보겠습니다. CSRF 공격을 시도하기 위해선 아래와 같은 몇 가지 조건이 필요합니다.
- 사용자가 보안이 취약한 서버로부터 이미 인증을 받은 상태여야 합니다.
- 쿠키 기반으로 서버 세션 정보를 획득할 수 있어야 합니다.
- 공격자는 서버를 공격하기 위한 요청 방법에 대해 미리 파악하고 있어야 합니다. 예상치 못한 파라미터가 있으면 불가능합니다.
위와 같은 조건이 만족되면 다음과 같은 과정을 통해 CSRF 공격이 수행됩니다.
- 사용자는 보안이 취약한 서버에 로그인합니다.
- 로그인 이후 서버에 저장된 세션 정보를 사용할 수 있는
sessionID
가 사용자 브라우저 쿠키에 저장됩니다.
- 공격자는 서버에 인증된 브라우저의 사용자가 악성 스크립트 페이지를 누르도록 유도합니다.
- 해당 악성 스크립트가 담긴 페이지를 클릭하도록 유도하는 방법은 다양한 것 같으나 몇 가지 유형을 정리하자면 다음과 같습니다.
- 게시판에 악성 스크립트를 게시글로 작성하여 관리자 혹은 다른 사용자들이 게시글을 클릭하도록 유도합니다.
- 메일 등으로 악성 스크립트를 직접 전달하거나, 악성 스크립트가 적힌 페이지 링크를 전달합니다.
- 사용자가 악성 스크립트가 작성된 페이지 접근시 쿠키에 저장된
sessionID
는 브라우저에 의해 자동적으로 함께 서버로 요청됩니다.
- 서버는 쿠키에 담긴
sessionID
를 통해 해당 요청이 인증된 사용자로부터 온 것으로 판단하고 처리합니다.
- 즉, 쿠키에 이미 sessionID가 담겨 있기 때문에 누군가 악성 스크립트로 요청을 강제하면 사용자의 요청과 무관한 요청이 이루어질 수 있음을 이용한 공격방법 입니다..
1.2.3 CSRF 방어 방법
- **Referrer 검증**
- Referer header는 Request에 포함된 요청을 하는 쪽의 웹페이지 urI를 나타낸다.
- Origin과 유사하지만 Origin은 Scheme, domain, port만을 의미하는데 반해 request path도 포함한다.
- Referer가 원하는 Referer가 아닌 경우 요청을 거부하는 방식으로 보안성을 강화할 수 있습니다.
- **CSRF 토큰 검증**
- 로그인 시 CSRF토큰을 생성하여 세션에 저장해두고 클라이언트 요청 시 이 값을 검증합니다.
- 세션을 저장해야하는 단점이 있습니다.
- 더 다양한 방법들
2. 위 내용들을 기반으로 Refresh Token을 어디에 저장하고 어떻게 구현할지 구체적으로 결정해 봅시다.
Refresh Token은 Cookie에 저장합니다.
2.1 XSS 공격을 방지.
- Local Storage는 XSS공격에 취약할 수 밖에 없습니다.
- Javascript코드로 탈취가 가능하기 때문입니다.
- 하지만, Cookie는 httpOnly flag를 줄 경우 Javascript코드로 조작이 불가능하기 때문에 XSS공격으로 인해 refresh token을 탈취하거나 조작하는것을 방지할 수 있습니다.
2.2 Cookie의 경우 CSRF에 취약하지만 이를 방지할 수 있습니다.
- 먼저 secure flag를 true로 설정하여 Https인 경우에만 브라우저에서 Cookie로 설정하여 활용하도록 합니다.
- SameSite=strict로 설정하여 CSRF를 방지할 수 있습니다.
- 이 경우 요청자와 Authorization Server가 같은 Site를 사용하는 경우에만 활용할 수 있습니다.
- 필자는 그렇지 않기 때문에 이를 none으로 설정하고 다른 방법들을 활용합니다.
- strict로 설정할 수 없는 경우 CORS Origin을 특정하여 다른 사이트에서의 요청이 불가능하도록 방지합니다.
- CORS는 요청한 서버의 Origin이 요청자의 Origin과 같지 않은 경우 서버에서 설정하여 응답 헤더로 적용된 Access-Control-Allow-* 헤더를 브라우저가 보고 응답 허용 여부를 결정하는 정책이다.
- 허용 Origin(프로토콜, 도메인, 포트의 종합), method, header등을 나누어서 설정할 수 있고 브라우저가 서버로 부터 응답을 받고 난 후 CORS 통과 여부를 결정한다.
- cors에 대한 자세한 내용 링크
- CORS로 방지하는 경우 한계점은 브라우저에서 응답을 받지 않는 것이지 요청 자체가 서버에서 처리되지 않는것은 아니기 때문에 좀 더 완벽한 CSRF방지를 위해서는 위에서 CSRF방지에 쓰인 여러 방법을 종합적으로 고려해야 합니다.
JWT인증 과정을 어떻게 구현해야할까요?
- 초기 로그인 시 Access token의 경우에는 클라이언트에 body, request param등으로 전달하여 Authorization header에 ‘Bearer {token}’으로 설정하도록 합니다.
- Refresh Token의 경우 응답의 Set-Cookie 헤더로 쿠키로 설정하도록 하고 httpOnly secure=true, sameSite 조건을 꼭 지정해줍니다.
- 매 요청시 JWT검증 과정 중 Access Token이 만료되었을 시에는 Refresh Token으로부터 Access Token을 재발급 받을 수 있는 API를 요청하도록 처리합니다.
- 이 API내에서 User를 조회하여 정지되지 않은 유저인지 확인하고 아닌 경우 Authority를 다시 확인하여 Access Token을 생성합니다.
- Refresh Token마저 만료되게 되면 재 로그인 요청을 시도하게 합니다.
- CSRF방지를 위해 Referer체크, CSRF토큰 검증등의 과정을 추가해야합니다.
3. 결론
- Refresh Token을 어디에 저장하는지에 대한 정해진 정답은 없지만, Cookie에 Refresh Token을 저장하는것이 가장 유리하다고 할 수 있습니다.
4. 출처