본문 바로가기

TIL

TIL) JWT 와 보안, CORS, 카카오에서 CORS

1. JWT 보안

jwt는 stateless. 서버에서 한번 보낸 토큰은 정상 토큰이라고 인식.

만약 access token의 만료 기간을 길게 잡아 이것만 사용하게 한다면
access token 이 탈취 되었을 때 서버에서 아무런 방어적인 행동을 할 수 없습니다.
서버는 해당 token이 탈취 되었다는 사실 조차 모를 수 있습니다. 

 

따라서 refresh token 같이 줌.
만약 refresh token이 탈취되어
해커가 새로운 access token을 요구해 발급받을 수 있습니다만, 이 발급이 진행되는 과정에서 다른 나라의

IP 주소로 요청이 들어왔다던가 또는 계정 도용으로 신고된 아이디이던가 등을 검증할 수 있는 작업을 

서버에서 할 수 있는 것입니다. 그러면 서버는 해커가 refresh token으로 요청한 것을 무시(거절)하고 해킹된

refresh token을 서버에서 지워버려 access token을 해커에게 발급하지 않을 수 있습니다.


액세스 토큰/리프레쉬 토큰은 보안성, 성능, 사용편이성 등등을 적절하게 타협한 결과입니다. 보안만 추구한다면 더 좋은 방법들이 많이 있습니다.
액세스 토큰을 무효화 처리하는 것도 당연히 가능합니다. 액세스 토큰이나 리프레쉬 토큰이나 본질적으로는 차이가 없으니까요. 하지만 액세스 토큰을 무효화 하는 것은 리프레쉬 토큰 무효화보다 성능에 더 영향을 줍니다. 1. 리소스 서버와 인증 서버가 분리된 경우, 매번 인증서버에 무효화 여부를 물어봐야 하고, 인증서버가 보틀넥이 됩니다. 2. 그렇지 않은 경우 역시, 토큰 무효화는 DB와 같은 서버 스토리지가 필요하고 따라서 latency에 영향을 줍니다.
따라서 저런 타협의 결과물로 일반적으로 액세스 토큰은 짧은 유효기간을 가지고 별도의 무효화는 지원하지 않습니다. 대신 리프레쉬 토큰은 긴 유효기간과 무효화를 지원합니다. 리프레쉬 토큰은 탈취되면 큰일이니, 항상 안전한 곳에 보관하고, https와 같은 안전한 전송수단만을 사용해야 하고, 액세스 토큰 발급시에만 전송해야 합니다. 예를 들어 액세스 토큰 유효기간이 1시간이라면 리프레쉬 토큰은 1시간에 한번만 사용되니, 탈취 가능성을 대폭 줄일 수 있습니다.


다만, HTTP 하이재킹으로 중간에 만료 시간이 지나지 않은 access token이 탈취될 수 있습니다.

이 때문에 HTTPS를 반드시 기본으로 사용하여 클라이언트와 서버 간에 전송되는 데이터를 암호화하는

것입니다. 

 

결론. 한 번 발급되면 정보가 변하지 않는 stateless라는 jwt 특성 때문에 refresh token을 사용하여 

access token을 발급한다. refresh token이라는 방어 도구라도 있어야 서버가 해킹된 토큰에 대한 방어 행동

을 취할 수 있게 된다 입니다.

https://okky.kr/article/1007579?note=2444715

 

 

 

클라이언트는 JWT 토큰을 어디에 보관할까?

HTML5 부터 Web Storage라는 개념이 도입되었다. 말그대로 웹 보관함이다. 
서버가 아닌, 클라이언트에 데이터를 저장하는 것이다.
Web Storage는 LocalStorage와 SessionStorage가 있다.

LocalStorage 같은 경우, 반영구적으로 보관하는 것인데, 브라우저를 종료해도(세션이 소멸되어도) 데이터는 유지된다. 다른 도메인은 LocalStorage에 접근하지 못한다.
SessionStorage의 경우, 각 세션마다 데이터가 별개로 저장된다. 세션을 종료하면 데이터는 자동 소멸된다. 같은 도메인이라도 세션이 다르면 접근을 하지 못한다.

일단 localStorage에 절대 보관하지말자. 해당 저장소에 보관하면 탈취될 우려가 있다.

결론은 Session Storage 또는 Secure Cookie에 보관하자.그러나 보관할 때 주의사항이 있다.
XSS/CSRF 공격으로부터 세션 스토리지는 안전하지 않다. XSS/CSRF 공격으로부터 안전하다는 가정하에 세션스토리지에 보관해야한다.
쿠키를 통해 보관할 때 XSS/CSRF를 통해 쿠키 탈취 또는 HTTP 통신할 때 쿠키가 노출될 우려가 있기 때문에 HTTP Only Cookie 설정과 HttpCookie 클래스의 Secure 속성을 true을 하여, HTTP에서 쿠키 전송을 하지 않게 하자.차라리 세션 스토리지보다 Secure Cookie에 보관하는게 안전할 수도 있다.

https://ehdvudee.tistory.com/15

 

JWT를 분석하고 사용해보자 (2/3)

해당 게시물은 시리즈이다. 목차 1편 : 개념 / 개요 / 프로토콜 설명 2편 : JWT(JWS) 보안 취약점 / 준수사항 분석 3편 : 실제로 구현해보자 JWT 실제 사용 및 보안적 측면 분석 해당 사항을 시작하기 전

ehdvudee.tistory.com

 

Http only Cookie

 

브라우저에서 쿠키에 접근할 수 없도록 제한하는 것.

document.cookie와 같은 자바스크립트로 쿠키를 조회하는 것을 막는 옵션.

 

XSS와 같은 공격이 차단.

HTTP Only Cookie를 설정하면 브라우저에서 해당 쿠키로 접근할 수 없게 되지만, 쿠키에 포함된 정보의 대부분이 브라우저에서 접근할 필요가 없음.

 

 

Secure 속성을 true

javascript 같은 브라우저단의 쿠키 탈취가 아닌 네트워크에 직접 접근하여 쿠키를 탈취하는 것을 방지하기 위함.

HTTPS가 아닌 통신에서는 쿠키를 전송하지 않음.

 

 

 

 

저런 얘기를 한 이유는 제가 생각하는 JWT의 가장 큰 장점은 stateless로 토큰을 검증할 수 있기 때문입니다. 말씀하신대로 사용자마다 디비를 조회하거나 레디스같은 세션 스토어에 키를 저장해 놓고 비교하면 revoke도 편하고 보안측면에서도 훨씬 강화할 수 있습니다. 이런 면에서 디비를 조회할 것이라면 굳이 JWT를 사용할 이유가 없다고 생각합니다.
블랙리스트나 화이트리스트로 JWT를 관리할 수도 있지만 그렇지 않은 경우에는 이미 발행한 JWT 토큰을 revoke할 수 있는 방법이 없습니다. 제가 이해한 내용에서는(그리고 이글 이후에 변경사항이 있어도 좇아가진 못했습니다.) JWT에서 로그아웃이라는 것은 클라이언트에서 토큰을 제거할 뿐이고 서버에서 해당 토큰을 revoke하는 것은 아닙니다. 그런면에서 다른 방법보다 JWT는 클라이언트에서 토큰을 어떻게 관리하는지가 중요하다고 생각했고 웹에서는 그런 부분에 취약하다고 생각했습니다.

https://blog.outsider.ne.kr/1160

 

JWT(JSON Web Token)에 대해서... :: Outsider's Dev Story

작년에 [JSON Web Token(JWT)에 대한 글](http://blog.outsider.ne.kr/1069)을 올렸다. 당시에는 JWT 처음 사용해 보면서 적은 글이라 그제야 약간 이해한 상황이었지만 시간이 지나면서 더 알게 된 부분도 있고..

blog.outsider.ne.kr

 

Outsider 님의 블로그의 의견을 요약해보면,

 

  • 웹의 로컬 스토리지는 앱의 로컬 스토리지만큼 안전하지 않다.
  • DB 에 리프레시토큰을 두고 검증을 하면 되지 않느냐?
    • 화이트 리스트 : 활성화된 토큰의 목록 DB
    • 블랙 리스트 : 비활성화된 토큰의 목록 DB
  • JWT 의 장점은 stateless 하게 토큰을 검증할 수 있다는 점.
  • 반면, 블랙리스트와 화이트리스트를 관리하지 못한다면 스토리지가 안전하지 못한 토큰에 대해 보안적으로 안전함을 확신할 수 없음.
  • 따라서 웹의 경우 토큰의 검증과 revoke 를 위해 화이트리스트와 블랙리스트를 관리해야함.
  • 하지만, 블랙리스트와 화이트리스트를 관리한다는 점은 stateless 하지 못함.
  • 결국 그럴 바에, 웹에서는 세션-쿠키 방식의 로그인이 낫다.

 

 

 

 

2. CORS

 

Cross Origin Resource Sharing

 

웹상의 이야기이다.

웹에서는 Image 테그, css 테그, script 테그, 동영상, Iframe 에 대해서 자유로운 요청이 가능한데,

스크립트 태그 내에서 특정한 도메인 간(Cross-Domain) 요청, 특히 Ajax 요청은 다른 도메인에 데이터를 요청하는 것이 금지

 

 

여기서 Cross-Domain 의 의미는,

IP 주소가 다르거나 Port 번호가 다른 곳에서 리소스를 가져오는 것을 금지한다는 것이다.

 

 

이전의 라이징캠프 과정은 안드로이드 or IOS 뿐이라 이 이슈가 없었지만, 이번에 웹과의 협업 과정이 추가되면서 이슈가 발생하여 다시 알아보게 되었다.

 

 

기본 동작

  1. 자바스크립트에서 브라우저로 Cross-Domain 으로의 fetch() (ajax) 요청을 실행시키게 한다.
  2. 브라우저는 Cross-Domain 서버로 요청을 보낸다.
    1. OPTIONS 메서드와 함께 요청할 리소스와 함께
    2. origin 필드에 Cross-Domain 요청을 보내는 출처 (요청을 보내는 곳)를 담고,
    3. host 필드에 요청할 리소스가 있는 서버의 호스트를 담는다.
  3. 서버에서는 Access-Control-Allow-Origin 필드에 서버에서의 Cross-Domain 허용 리소스들을 담아서 보낸다.
  4. 브라우저에서는 이 Access-Control-Allow-Origin 필드의 값과 요청을 보내려 했던 값을 비교하여 교차출처 리소스 공유 정책에 위반되는지 확인한다.
Http 메서드 OPTIONS

브라우저가 서버에게 지원하는 옵션(메서드든 호스트든)들을 미리 요청하고 허가된 요청을 받는 메서드.
서버와의 연결시 허가된 요청만을 설정하기 위한 목적.

 

 

 

시나리오

https://ko.wikipedia.org/wiki/%EA%B5%90%EC%B0%A8_%EC%B6%9C%EC%B2%98_%EB%A6%AC%EC%86%8C%EC%8A%A4_%EA%B3%B5%EC%9C%A0

위 시나리오는 MDN 에서 소개하고 있는 simple request 라는 CORS 의 시나리오이다.

 

CORS 의 시나리오에는 크게 세가지가 있다.

 

  • Preflight Request
    • 예비 요청을 먼저 보낸다. 예비 요청은 이 Cross-Domain 요청이 보내도 되는 즉, CORS 에 허용되는지 먼저 검사는것이다.
  • Simple Request
    • 위의 시나리오 이다. 일부 조건이 맞을 때, 위와는 다르게 예비 요청을 Skip 할 수 있다.
  • Credentialed Request
    • 기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다. 이때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션이다.
    • 다른 출처 간 통신에서 좀 더 보안을 강화할 수 있다.

 

SOP - Same Origin Policy 같은 출처의 리소스만 공유할 수 있는 규칙.

같은 출처의 리소스만 공유하기는 너무 제약이 심하므로, SOP 의 예외 조항격인 CORS 를 만들었다.
SOP 를 위반한 것 중, CORS 를 지키는 것이면 괜찮지만,
CORS 를 위반 (CORS 를 위반했다는 것은 SOP 도 위반.) 하면 에러를 내게 되는 것이다.

 

https://evan-moon.github.io/2020/05/21/about-cors/#preflight-request

 

CORS는 왜 이렇게 우리를 힘들게 하는걸까?

이번 포스팅에서는 웹 개발자라면 한번쯤은 얻어맞아 봤을 법한 정책에 대한 이야기를 해보려고 한다. 사실 웹 개발을 하다보면 CORS 정책 위반으로 인해 에러가 발생하는 상황은 굉장히 흔해서

evan-moon.github.io

 

 

 

서버 개발자 입장에서는, 허용할 리소스에 대해서만 Access-Control-Allow-Origin 를 지정해주는 걸 신경쓰면 될 거 같다.

 

 

 

 

 

 

 

3. 카카오로그인에서의 CORS

프론트엔드 스크립트에서 kapi 는 cors 가 막혀있음.

kauth 는 열려있지만, Redirect URL 로 돌아가는 로그인, 로그아웃은 막혀있음.

 

=> 인가코드를 받는 과정까지 웹에서 가능하고, 이후 인가코드를 서버로 보내서 서버에서 엑세스토큰을 받고 엑세스토큰으로 사용자 정보를 받는 순서로 해야함.

 

https://kakao-tam.tistory.com/81

 

앱에서는 cors 문제가 발생하지 않아 이전까지 앱에서 액세스 토큰을 받아 처리할 수 있었음.