때는 바야흐로
프로젝트 달록
에 카테고리 관리자 기능이 없던 시절....
아래처럼 카테고리에 일정을 추가할 수 있는 사람은 카테고리를 생성한 사람뿐이었습니다.
달록 사용자들이 관심있어 할 만한 카테고리들을 만들어 일정을 추가하기 위해 카테고리를 생성한 팀원의 accessToken을 받아 로그인을 했습니다.
🤦♂️ 순간 띵 하면서 남의 accessToken을 탈취하면 어떤 일이든 할 수 있겠다는 위험을 깨달았어요.
발생할 수 있는 위협에 대해서 알아보고 현재 달록 프로젝트에서는 어떤지 찾아봤어요.
검색해보니 XSS, CSRF 같은 취약점들이 발생할 수 있겠더라구요.
그래서 먼저 보안 위협에 대해 공부하고 프로젝트에서 고찰해봤어요.
저희 달록 서비스에서는 JWT를 사용하고 있어요.
JSON 포맷을 이용하여 사용자에 대한 속성을 저장하는 Web Token입니다.
스크립트를 통해 html이나 js를 동작시켜 사용자의 인증 정보를 탈취해요.
공격자가 유저 정보(accessToken)를 탈취해오는 악성 스크립트
를 사이트에 삽입해요.
사용자가 해당 악성 스크립트가 들어있는 페이지를 들어가요.
스크립트가 실행되어 유저 정보가 탈취됩니다 (⊙ˍ⊙)
브라우저 저장소(localStorage, sessionStorage)가 가장 취약해요.
개발자들이 쓰기도 쉽지만 공격자들도 쓰기 쉽죠ㅎㅎ;;
Storage.getItem('...')
✋ 올~ 쿠키는 HttpOnly를 붙여주면 스크립트로 접근할 수 없어서 XSS 공격에서는 안전해요.
Set-Cookie: 쿠키명=쿠키값; path=/; HttpOnly
React
는 기본적으로 Escape
를 진행해줍니다.인증된 유저가 의도하지 않게 서버에 공격을 하게 만들어요.
사용자가 정상적인 웹 페이지에 로그인하여 쿠키가 저장됩니다.
이후 사용자가 (악성 페이지 인지를 못하고) 악성 웹 서비스에 들어갑니다.
악성 페이지는 만들어졌던 쿠키로 서버에 악의적인 요청(DB의 수정/삭제, 회원정보 수정 등)을 보냅니다 (⊙ˍ⊙)
쿠키가 취약해요.
HttpOnly 옵션을 주어서 XSS 공격을 막았지만 CSRF는 다른 이야기죠(⊙o⊙)
secure
접미사를 붙입시다.Set-Cookie: 쿠키명=쿠키값; path=/; secure
👍 secure 접미사를 사용하면 브라우저는 HTTPS가 아닌 통신에서 쿠키를 전송하지 않아요.
원하는 도메인에서만 request를 수용
할 수 있어요.요청은 가능합니다.
많은 고민을 진행한 결과 localStorage와 sessionStorage를 아예 사용하지 않는 방안을 생각하게 되었습니다.
React에서 자동으로 escape를 진행시켜준다고 해도 제가 알지 못하는 어떤 위협들이 존재할지 모르기 때문이죠.
refreshToken
을 반드시 도입해야겠더라구요. 아래에서 더 살펴봅시다!localStorage와 sessionStorage
는 스크립트 코드로 브라우저 상에서도 너무나도 쉽게 탈취될 수 있어요.
httpOnly Cookie
에 accessToken을 사용한다면 스크립트로 인한 직접적인 탈취는 막을 수 있겠지만 DB를 수정/삭제하는 요청 자체를 보낼 수는 있습니다.
👍 그래서 브라우저에서 쉽게 접근할 수 없는 javascript 변수에 accessToken을 담기로 했어요.
refreshToken을 httpOnly Secure Cookie에 저장한다고 해봅시다.
✌ httpOnly
✌ Secure
🤔 CSRF에서는 어떨까요?
🥵쿠키는 브라우저에서 공용으로 사용되기 때문에 악성 사이트에서 접근 가능해요. 이 경우 쿠키에 담긴 refreshToken으로 서버에 어떤 요청을 보낼 수 있겠죠.
Access-Control-Allow-Origin
설정을 하여 특정 도메인에서 오는 요청들만 받을 수 있거든요!🥵물론 악성사이트에서 Referrer를 쉽게 수정하여 서버의 Origin 허용점을 쉽게 뚫을 수는 있어요.
🎉 아하 이러니 refreshToken을 도입해야겠군요.
하지만 늘 장점만 존재하는 것은 아니죠. 반드시 trade-off가 있기 마련입니다.
웹 서비스에 처음 접근할 때, 새로고침할 때에는 refreshToken을 사용하여 accessToken을 반드시 받아야하기 때문에 이 부분에서 네트워크 요청이 필히 발생
하게 됩니다.
이로 인해 사용자는 초기 로딩이 느리다고 느낄 수 있겠죠.
위의 방법이 최선이 아님을 인지하고 더 나은 방향을 찾아봐야겠다고 생각이 드네요.