개발 지식/웹

로그인 인증 부수기

트리맨스 2022. 10. 23. 02:31
반응형


최근들어 유저의 인증 로직 관련해서 보완해야 할 점이 보여서 수정 중, 이참에 로그인 인증 관련해서 한번 정리해 보고자 글을 작성한다.

배경 지식


먼저 인증과 인가에 대해서 구분을 하자. 인증(Authentication)은 클라이언트가 주장하는 사용자가 맞는가를 검증하는 과정이다. 인가(Authorization)은 인증 이후에 하는 작업으로, 인증된 사용자가 접근할 수 있는 자원의 확인 절차이다.
예를 들자면 로그인 자체는 인증에 해당된다. 내가 이 서비스의 회원이다는 것을 서버에 요청하고, 이를 확인하는 것이 인증이다. 인증 이후에 내가 게시물을 작성한다면, 나와 관리자를 제외한 다른 유저는 내 게시물을 수정하거나 삭제할 수 없다. 이것이 인가이다.

다음으로 HTTP의 비연결성과 무상태성에 대해서 정리하자.
비연결성은 서버에 요청하고, 서버에서 응답을 받게 되면 끝이다 라는 것이다. 지속적으로 통신을 하지 않는다는 것이다. 주기적으로 요청을 보내서 실시간 동작 처럼 사용하는 방법도 있으나, 이는 서버의 자원이 매우 빠르게 소모되는 행동이다. 실시간 작업을 하고 싶으면 웹소켓이나 훅을 사용하는 편이 낫다.
무상태성은 클라이언트가 이전에 무엇을 했는지, 인증을 했는지 등등에 대해서 알 수 없다는 것이다. 클라이언트가 방금 전에 인증 절차를 거쳤어도, 다음에 요청할 때는 이전에 인증한 것과 아예 독립적인 사건으로 바라본다는 개념이다.

여기서 로그인에 대한 필요성이 나온다. 무상태성의 관점에서 보면 사용자 입장에서 인증을 마쳤는데, 다음 요청에서 또 인증을 요청하게 도면 사용성이 매우 구려질 것이다. (웹사이트를 클릭할때마다 로그인 하라고 하면...지옥 그 자체) 그렇다면 사용자의 아이디와 비밀번호를 브라우저에 저장하고 매 요청 마다 보내면 되지 않을까? 하는 생각이 들 수도 있다. 가능하긴 하나, 클라이언트에 민감한 정보가 있게 되면 보안에 취약할 수 밖에 없다.

이러한 문제를 해결하기 위해서 세션 또는 토큰을 활용하여 문제를 어느정도 해결하는 기법이 나와 있다.

세션기반 인증


세션기반 인증은 사용자의 인증 정보가 서버의 세션 저장소에 저장되는 방식이다. 순서는 다음과 같다.

  1. 사용자 로그인
  2. 인증 정보를 서버의 세션 저장소에 저장
  3. 저장된 세션 정보의 식별자인 Session Id 를 발급한다. Session Id 는 쿠키 형태로 저장되지만, 실제 인증 정보는 서버에 저장되어 있다.
  4. 브라우저는 이후의 요청마다 http cookie 헤더에 Session Id 를 함께 서버로 전달한다.
  5. Session Id에 해당하는 세션 정보가 있다면, 서버에서는 인증된 사용자로 판별한다.

 

토큰기반 인증


토큰기반 인증은 사용자의 인증 정보를 클라이언트에 저장하는 방식이다. 인증 정보는 로컬 스토리지 or 쿠키로 저장이 된다.

  1. 사용자가 로그인한다.
  2. 해당 유저와 매칭되는 JWT 토큰을 반환한다. 이 때 토큰은 서버의 개인키에 기반해 암호화되어 반환한다.
  3. 클라이언트는 해당 토큰을 저장하고 있다가, 다음에 서버에 요청할 때 JWT 토큰을 담아서 준다.
  4. 서버는 JWT 토큰이 정상인 동시에 만료 시각이 지나지 않았으면 인증된 사용자로 판별한다.

JWT는 아래에 간단하게 정리를 해 두었다.

그래서 뭐가 더 나음?


세션 인증과 토큰 인증을 비교해보자.

1. 용량

  • 세션은 쿠키 헤더에 Session Id만 실어서 보낸다. 그래서 크기가 매우 작다.
  • 토큰을 암호화된 문자열을 전송하므로, 크기가 매우 크다

2. 보안

  • 세션은 모든 인증정보를 서버에서 관리한다. Session Id가 유출되어도 서버에서 해당 세션을 무효처리 하면 된다.
  • 토큰은 유출되면 망한다. 또한 별도 암호화 (시그니쳐만 확인할 뿐, payload는 볼수 있음) 가 없어서 민감한 정보를 담을 수 없다.

3. 확장성
서버는 대부분 수평 확장 방식을 사용한다. (로드밸런서로 트래픽 분산하면 되니까) 이로 인해 세션에서는 약간의 문제가 생긴다.

  • 세션은 세션 스토리지를 따로 분리하는 등의 추가 작업이 필요함.
  • 토큰은 그런거 없다. 왜냐하면 클라이언트에 키가 있기 때문이다.

 

결론

  • 서버 입장에서 보면 토큰 방식이 서버 확장에 유리하다. 하지만 보안 면에서 세션 방식보다 불리함. 그러나 대부분의 사이트에서는 세션 방식이 서버에 부하가 많이 가므로, 대부분은 토큰 방식을 사용한다. 이 포스트에서는 내가 사용중인 JWT 기반으로 설명할 예정이다.
  • 보안 입장에서 보면 세션 방식이 좋다. 하지만 서버 확장 측면에서 보면 서버의 자원이 많이 든다.

 

JWT 토큰이란


토큰 인증 방식은 JWT토큰 방식을 많이 사용한다.
JWT 토큰은 크게 Header, Payload, Signature로 이루어진다.

Header는 해시 알고리즘과 토큰의 타입, Payload는 클라이언트 사용자의 정보, Signature는 헤더 + 내용 + 서버의 개인키 결과를 해시 알고리즘을 거친 전자서명 결과물이 있다.
JWT 토큰은 Payload를 암호화 하지 않는다. 그래서 Payload에는 민감한 정보를 두면 안 된다. 그리고 Signature를 서버에서 확인하여 해당 JWT토큰이 유효한지를 판별하게 된다.


JWT 토큰인증


앞서 말했듯이, 토큰기반 인증은 발급된 토큰이 해커에게 탈취되면 대처가 매우 어렵다. 그래서 보완한 방법이 토큰의 유효기간을 짧게 해서 보안을 강화하는 것이다. 하지만 이렇게 하면, 사용자는 토큰이 만료될 때 마다 로그인을 계속 해줘야 한다. 이는 사용자 입장에서 매우 비효율적이다.
이 문제를 해결하기 위해서 새로운 토큰이 사용된다. 이를 Refresh Token 이라고 한다. 반대로 처음에 말했던 토큰을 Access Token이라고 한다.
그렇다면 Refresh Token으로 어떻게 보안을 유지하는가? 순서는 다음과 같다.

  1. 로그인을 할 때 서버에서는 Access Token과 Refresh Token을 발급한다.
  2. 서버는 Refresh Token을 DB에 보관한다. 클라이언트는 Access Token을 헤더에 등록, Refresh Token을 클라이언트의 안전한 곳에 보관한다.
  3. Access Token이 만료되었으면 클라이언트는 서버에 Refresh Token을 보내서 새로운 Access Token을 받아온다. 또는 새 Refresh Token을 받아온다. 어쨋든 결과적으로는 유저의 로그인이 계속 지속되게 하는 것이다
  4. 서비스에 접속중인 경우에 세션이 만료되는 것을 방지하기 위해 주기적으로 토큰을 갱신할 수 있게 하는 로직을 클라이언트에 설정할 수도 있다.


위의 로직대로 작성하면 다음과 같은 장점이 있다.

  1. Access Token의 만료기간을 짧게 잡으면서도 세션을 비교적 길게 유지할 수 있다.
  2. Refresh Token은 서버의 DB에 있으므로, 원하는 시점에 Refresh Token을 삭제함으로서 세션을 강제 만료 시키는 것이 가능함.

 

의문점

그런데 자세히 보면, 해당 로직이 완벽한 절차는 아닌거 같아 보인다.
Refresh Token이 탈취당했을 경우에는 기존의 문제에서 벗어나지 못한다. Refresh Token이 Access Token보다 안전하다고는 하지만, 노출 빈도가 적어서 그렇지 탈취당했을 경우에는 똑같이 위험하다. 저장하는 방법에 따라서 XSS, SCRF 등의 보안 이슈로 이어지게 된다. 해당 보안 이슈는 하단에 적어 두었다.
다음으로는 Refresh Token이 DB에 있을 경우, 속도에 문제가 있을 수도 있다. 이러한 문제는 Redis 같은 DB를 사용하면 해결이 된다.

-> 사실 현대 보안 기술은 공격자가 마음만 먹으면 뚫을 수 있다고 생각한다. 어떻게 보면 창과 방패의 대결이고, 토큰의 방법도 완벽한 보안을 제공한다고 보장은 못한다. 하지만 보편적인 인증 방식으로는 충분히 실사용 가능한 보안 방식이다. 결론은 컴퓨터 바이러스 검사 잘 하고, 항상 해킹에 대비하고 주의해야 하는 클라이언트 사용자의 노력이 필요해 보인다.

Refresh Token과 관련된 보안 이슈


앞에서 Refresh Token과 관련된 간단한 이슈를 정리해 보았다.

XSS

웹 해킹 공격중의 하나이다. Cross Site Scription의 약자이며, css라는 단어가 이미 있어서 xss로 대체해서 사용한다.
이는 게시판이나 웹 메일 등에 js코드를 삽입해서 해당 페이지를 열 때 악의적인 동작이 실행되도록 하는 공격이다. reflected xss는 악의적 공격 스크립트가 있는 url을 누르게 유도하는 방식이고, stored xss는 웹 사이트의 게시판 같은 곳에 스크립트를 삽입하여, 해당 페이지를 열 때 스크립트가 실행이 되는 방식이다.
이를 이용해서 해커는 앞에서 말한 쿠키 및 세션ID 획득, 악성코드 다운 등의 악의적 행동을 할 수 있다. 이를 방지하기 위해서는 클라이언트 웹 페이지에서 해당 스크립트 실행을 방지하는 장치를 마련해야 한다.

CSRF

Cross-Site Request Forgery의 약자이다. 사이트 간 위조 정도로 해석하면 되겠다.
사용자의 의지와 무관하게 공격자가 의도한 행위를 하게 만든다. 특정한 API 호출 위해 링크 클릭을 유도하거나, 이미지 태그의 링크를 API 앤드포인트로 만드는 등의 작업을 한다. 여담으로 08년도 옥션 해킹 사고도 이것으로 일어났다고 한다.
이같은 공격을 방지하기 위해서는 중요한 API는 도메인 필터링 사용, SCRF Token을 사용해서 정상적인 요청인지 서버단에서 검증, 캡챠 사용, form 태그 입력 시 POST 방식 등을 해주면 좋다.


로그인 인증 부수다 내가 부서지게 생겼다. 아래는 참고 링크들이다.


https://velog.io/@kingth/서버-인증-방식세션쿠키-토큰
https://4rgos.tistory.com/1
https://sj602.github.io/2018/07/14/what-is-CSRF/
https://zzang9ha.tistory.com/341
https://hshine1226.medium.com/localstorage-vs-cookies-jwt-토큰을-안전하게-저장하기-위해-알아야할-모든것-4fb7fb41327c

반응형

'개발 지식 > ' 카테고리의 다른 글

웹사이트 접속 시에 무슨 일이 벌어지는가?  (0) 2022.11.19
CORS 정리하기  (0) 2022.10.24