인증(Authentication), 인가(Authorization) 그리고 로그인

조현근·2022년 11월 9일
0
post-thumbnail

인증(Authentication)

  • 식별가능한 정보로 서비스에 등록된 유저의 신원을 입증하는 과정
  • 로그인

인가(Authorization)

  • 인증된 사용자에 대한 자원 접근 권한 확인
  • (로그인 후) 글 작성(성공)
  • (로그인 후) 다른 사람의 글 수정(실패)
  • 인증이 선행되어야 인가가 있음

로그인 구현하기

인증이 필요한 기능이 있다. 속닥속닥 서비스에선 글을 작성하려면 로그인이 선행되어야 한다. 글을 작성하려면 username, password를 서버에 보내 유효한 사용자인지 확인해야 하는데 글 작성할때마다 이런 행위를 거치면 사용자 경험이 매우 떨어지게 된다. 어떻게 하면 한 번 로그인을 하면 계속 로그인된 상태를 유지할 수 있을까?

클라이언트에 username, password을 저장하고 권한이 필요한 기능일때 같이 보냄

클라이언트(프론트엔드)에서 username, password를 저장해놓고 권한이 필요한 기능을 사용할때 함께 보내는 방법을 사용할 수 있다. 그러면 사용자는 매 번 글을 작성할때마다 번거로운 로그인과정을 거치지 않아도 된다.
하지만 크게 2가지 보안적 이슈가 있다.

  1. 네트워크를 통해 매 번 username, password가 노출된다.
  2. 클라이언트에 저장된 username, password가 노출될 수 있다.
  3. 악의적인 사용자가 암호화된 데이터를 탈취, 이용해 기능을 사용할 수 있다.

1번 2번 모두 암호화를 통해 해결할 수 있을 것이다.
하지만 대칭키 암호화 방식을 사용한다면 2번과 같은 문제로 대칭키가 탈취당할 수 있고 비대칭키를 사용한다면 서버에서 복호화하는데 시간이 오래걸릴 것이다.
게다가 3번이 가장 크리티컬한 문제라고 생각하는데 username, password는 한번 정해지면 거의 바뀌지 않는 정보이다. 암호화되었더라도 해커가 이를 탈취해 사용하면 권한이 있는 사용자처럼 기능을 이용할 수 있을것이다.

중요 정보 노출을 최소화 한다.

따라서 매번 username, password와 같은 중요 정보를 이용하는 방식은 보안적으로 문제를 야기시킬 수 있다.
중요 정보가 아닌 탈취되어도 되는 정보를 이용하고, 해당 정보를 자주 갱신시켜준다면 보안 이슈를 많이 줄일 수 있을 것이다.
크게 2가지 방법을 알아보겠다.

세션

세션은 서버에서 로그인 된 사용자를 관리하는 방식이다.
사용자가 로그인을 하면 서버에선 유일성이 보장되는 식별자(ex. UUID)를 매핑하고 클라이언트와 통신에선 식별자를 이용해 로그인을 유지하면 된다.
식별자 자체는 아무 의미가 없기 때문에 탈취되더라도 중요정보를 알아낼 수 없고, 해커에게 탈취되더라도 서버에서 해당 세션 데이터를 날려 대응을 할 수 있다.
물론 단점도 존재한다. 해커에게 탈취된 세션을 구분하는게 까다롭고, 악의적인 사용을 막기 위해 세션의 수명을 짧게 가져간다면 사용자는 자주 로그인해야 하는 문제가 발생한다. DB를 사용하면 데이터를 접근하고 확인하는데 시간이 오래 걸릴 것이고 모든 권한 요청이 DB에 몰리게 된다면 병목현상이 일어날 것이다. redis와 같은 인메모리를 사용하면 WAS가 여러대일때 데이터 일관성을 맞추는 문제를 고려해야 할 것이다.(redis나 인메모리는 추가적인 학습이 필요함... 적힌 내용이 정확하지 않을 수 있음)

토큰(JWT)

토큰은 일반 토큰 기반과 클레임 토큰 기반이 있다.
일반 토큰 기반은 토큰을 검증할 때 DB를 통해 검증한다.
클레임 토큰 기반은 사용자 인증에 필요한 모든 정보를 토큰 자체에 담고 있기 때문에 따로 DB에 접근하지 않는다. 클레임 기반 토큰인 JWT에 대해 중점적으로 이야기해보자.

JWT(Jason Web Token)

JWT는 세 가지 파트로 구성된다. '헤더(Header)', '페이로드(Payload)', '서명(Signature)'이다.
'헤더'엔 어떤 해시 알고리즘으로 암호화 할 것인지, 어떤 토큰을 타입을 사용할지에 대한 정보를 가지고 있다.
'페이로드'엔 사용자 정보가 들어간다. 물론 password같은 민감한 정보가 아닌 가벼운 정보가 들어간다. 이런 정보의 한 조각을 클레임(Claim)이라 한다.
'서명'엔 Header, Payload를 Base64 URL safe Encode를 한 후 Header에 명시된 해시함수를 적용하고, 서버의 개인키로 서명한 데이터가 들어간다. 즉, '서명'은 어디서도 열어볼 수 있지만(서버의 공개키를 이용해서) 서버에서만 생성할 수 있는 데이터인 것이다.

검증
JWT 검증은 '서명'을 서버의 공개키로 열어 해시값을 확인하고, Header, Payload를 해싱해 둘이 같은지 비교하는 방식으로 이루어진다. 둘이 같으면 유효한 토큰이고 다르면 유효하지 않은 토큰인 것이다.
또한 토큰의 유효기간을 확인해 유효기간이 지났으면 유효하지 않은 토큰이 된다.

문제
JWT는 치명적인 보안적 문제가 있다. 서버에서 JWT를 관리하지 않으니 토큰이 탈취되면 서버에선 아무런 대응도 하지 못하는 것이다.
JWT 토큰 기반 access token은 매번 네트워크를 통해 전달되기 때문에 탈취에 취약하다. 탈취된 토큰은 아무런 제재없이 사용이 가능하다. 토큰이 탈취되었는지 판단하기까지 많은 시간이 걸리고, 탈취되었음을 알았을때도 서버에서 할 수 있는 방법이 거의 없다. 어떤 방법을 사용하더라도 네트워크에 매번 노출되는 access token은 탈취에 취약하기 때문에 근본적인 해결책이 되지 못한다. 결국 access token의 생명주기를 짧게 잡아야 하는데 이러면 사용자 편의성이 매우 떨어진다.

이런 단점을 보완하기 위해 refresh token이 사용된다.
access token의 생명주기를 매우 짧게 잡아 탈취 시 위험부담을 낮추고 대신 생명주기가 긴 refresh token을 통해 사용자 로그인 없이 access token을 재발급해주는 매커니즘이다.

  1. 사용자가 로그인에 성공하면 서버에서 access token, refresh token을 생성하고 클라이언트에 보낸다. 그리고 서버에선 refresh token을 저장한다. access token엔 사용자정보, 만료기간이 들어있고, refresh token엔 만료기간만 들어있다.
  2. 클라이언트는 access token을 이용해 요청을 보낸다.
  3. access token이 만료되면 서버에선 401 응답을 보낸다.
  4. 클라이언트는 401 응답을 받으면 access token과 refresh token을 같이 보낸다.
  5. 서버에선 access token을 이용해 사용자정보를 추출하고 이를 이용해 DB에서 클라이언트가 전달한 refresh token이 유효한지 검사한다.
  6. 유효한 refresh token이면 access token을 재발급해준다.
  7. refresh token이 만료되었으면 재로그인을 요청한다.

refresh token은 네트워크에 거의 노출되지 않는다. 따라서 탈취 위험이 적다. access token은 네트워크에 자주 노출되지만 생명주기가 짧다. 그래서 탈취로 발생할 위험을 줄일 수 있다.
refresh token이 탈취당하면 DB에서 refresh token 정보를 삭제하면 된다.
access token이 탈취되었거나 refresh token이 만료되었는데 access token의 생명주기가 남아있다면 블랙리스트를 통해 access token을 제어할 수 있다.

단점
보안은 항상 서버에서 확인을 해야 한다. 그렇지 않으면 어찌됐든 보안 허점이 발생한다. 클라이언트에 저장된 refresh token이 탈취당하면 결국 보안허점이 생긴다. 탈취당한 토큰인지 파악하는 건 쉽지 않다. 우아한테크세미나의 애플리케이션 보안을 보면 보안을 위해선 jwt를 지양해야 한다고 한다.

출처

https://velog.io/@hahan/JWT%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80
https://www.youtube.com/watch?v=RQv86D0M5YY&list=PLgXGHBqgT2TtGi82mCZWuhMu-nQy301ew&index=2
https://sokdak-sokdak.tistory.com/11

profile
안녕하세요!

0개의 댓글