안녕하세요. 뽀각팀 백엔드 개발자 이준영입니다.
저는 오늘 소셜로그인을 맡아 개발해온 얘기를 해보고자 합니다.
- 로그인 도입기
프로젝트를 프론트로도 백으로도 해보면서 한번도 소셜로그인 쪽을 맡아서 해본 적이 없었기에, 이번에는 꼭 소셜 로그인을 정복해보겠다는 다짐으로 소셜 로그인쪽 구현을 맡겠다고 했습니다. - 마주친 문제점들
세션 방식의 로그인과 토큰방식의 로그인 차이점부터 시작해, Refresh Token을 쓰는 이유, OAuth와 OpenID Connect의 차이는 뭔지, OAuth1.0과 OAuth2.0은 어떻게 다른지, 소셜로그인 후 로그인 서버에서 access Token을 받아올 때 서버로 받아와야하는지 클라이언트로 받아와야하는지… 등등등..
호기롭게 소셜로그인을 맡았지만, 위의 이 모든 것들이 제가 프로젝트를 하면서 새롭게 배운 것들이었습니다. 그 만큼 로그인의 프로세스와 어떤 것이 있는지 정확하게 알지 못했었고, 그러다보니 소셜로그인을 구현하는데 많은 시간을 들여야했습니다.
그 중에서 오늘은 access Token과 refresh Token을 어떻게 클라이언트에게 주어야하고, 클라이언트는 이걸 어떻게 보관해야하나? 에 대해 말해보고 싶습니다.
저희 팀의 기존 방식은
- responseBody에 accessToken값과, refreshToken값을 다 보내주고,
- 클라이언트에서 각각을 저장하고 있다가 요청헤더에 Authorization값으로 accessToken값을 보내준다.
- 그리고 만약 accessToken가 만료됐다면, 만료됐다는 코드를 보내주고
- 클라이언트는 다시 RT 재발급 api로 재요청 => 새로워진 AT와 RT를 획득!
⇒ 이 방식은 쿠키를 사용하지 않으므로 CSRF(크로스 사이트 요청 위조)공격으로 부터 안전합니다. 하지만 AT와 RT를 클라이언트가 직접 관리하게 됩니다. 따라서 localStorage, sessionStorage에 저장할 경우 악성 스크립트가 해당 토큰에 접근할 수 있어 XSS-크로스사이트 스크립팅 공격의 우려가 있었습니다.
그러던 중, http-only secure쿠키를 쓰는 방식을 보게되었고 쿠키를 사용하는 방법에 대해 고민해보게되었습니다.
결론적으로는, 안전한 로그인을 위해서는 원래의 방법이 아닌
- accessToken → 메모리 (JavaScript 변수 내)
- refreshToken→ 쿠키에 저장
위 처럼 저장하는 것이 좋다고 생각했습니다.
그 이유는 다음과 같습니다.
- accessToken 는 쿠키에 저장하는게 안전하지 않다! → CSRF 공격 때문에 → 클라이언트에서 저장하는 방법 중 가장 안전한 메모리에 저장하자.
- refreshToken 은 클라이언트가 저장하는 방법 중 앞에서 말했던, accessToken을 저장했던 메모리나 session storage에는 저장 못한다. (계속 보관되어줘야 사용자가 재로그인 할 필요없이 로그인이 유지되기 때문에)→ 그럼 local Storage나 쿠키! 둘 중에 뭐가 좋을까?
- local Storage의 단점 : XSS 공격 → 막을 수 없음
- 쿠키의 단점 CSRT 공격 → sameSite설정을 통해 해결할 수 있음. & 설령 CSRF 공격이 발생하더라도, refreshToken으로 토큰을 재발급받는 과정에서 응답이 해커가 아닌 정상적인 사용자의 주소로 반환되기 때문에 비교적 안전하다.
짧은 지식이지만, 프로젝트를 하며 고민했던 부분을 공유해보았습니다.
뽀각 앞으로도 파이팅-!