Web3는 인증 구현을 어떻게 할까? [ TIL / node.js]

알락·2023년 7월 16일
1

banner

인증구현이라는 시련이 다시 다가왔다. express로 인증구현을 할 때는 jwt 라이브러리를 사용해서 쿠키의 형식으로 클라이언트의 인증을 구현한 적이 있다. 하지만 이번 구현은 nest.js라는 상차림을 하고, passport.js라는 숟가락을 얹었다. 여기에 블록체인 지갑 연결까지 더해서 참으로 재밌는 시도가 되었다.(이렇게 덤덤하게 말하고는 있지만 정말 힘들었다.)
특히 이번 글을 통해서는 Web3에서 인증구현은 어떻게 이루어지는 지에 대해서 고민해보고 글을 작성해보고 있다. 이번 과정을 통해서 인증이라는 행위와 Web3의 특징인 지갑이라는 수단을 이용하는 것에 대해서 깊이 고민해보는 기회가 되었다.

블록체인과 지갑

특히 이번에 포커스를 두고 포스팅을 해볼 주제는 Web3다. 블록체인에 올려져 있는 자산을 제어하기 위해서는 클라이언트 지갑과의 상호작용을 하게 된다. 여기서 클라이언트는 단말기를 들고 있는 유저라고 생각하자.

public-private key

지갑은 유저의 키 쌍을 보관한다. 하나는 Public Key고, 하나는 Private Key다. 블록체인의 자산에 대한 거래를 하기 위해서는, 1. 소유와, 2. 소유자가 직접 거래를 실행했다는 것을 증명해야한다. 이를 블록체인에서는 공개키-개인키, 즉 위에서 언급했던 Public Key-Private Key 기법을 사용해서 문제를 해결한다. Private Key를 통해 서명한 거래 데이터를 미검증 거래 데이터들만 모아 놓는 저장소(Transaction Pool)에 전송해 놓으면, 블록체인의 노드들이 거래 데이터를 검증하고, 블록으로 만들어 확정시킨다. 그럼 비로서 원 소유자가 의도했던 자산에 대한 제어가 블록체인 상에서 공식적으로 이루어지게 된다.

process public-private key

Private Key를 통해 서명된 데이터는 서명에 이용했던 데이터(message)과 정교한 암호학으로 쌍이 되는 Public Key를 도출할 수 있다. 그래서 보통 Public Key는 말 그대로 공공에게 노출해도 되는 이를테면 암호학적인 본인의 이름이 될 수 있다. 하지만 본인을 정말 본인이게 하는 본질은 Private Key다. Private Key로 메세지들을 서명하기 때문에, Private Key는 절대 노출되어서는 안된다.

위에서 설명한 기본적인 블록체인의 구조를 이용해서 Web3에서의 인증을 구현해 볼 것이다.

인증

기존에도 웹 혹은 앱 서비스를 이용하는 유저들이 각자 본인임을 인증하기 위해서 여러 수단이 제시되었다. 당장 나열해도 쿠키/세션, JWT, 그리고 OAuth가 있는 것 같다. 이게 다 HTTP가 stateless한 덕분이다. 각각의 인증에 대해서 소개하는 것은 이번 포스트에서 다루는 것은 생략하려고 한다. 필자가 집중해서 조명해보고 싶은 것은 지금까지 제시된 인증 방법의 큰 그림을 그려보는 것이다.

old auth

쿠키/세션과 JWT 같은 경우는 서비스를 제공자가 운영하는 서버에 유저 데이터를 저장해두고 인증이 필요할 때마다 서버에 요청하게 된다. OAuth도 마찬가지로 서버에 인증을 요청하지만, 서버에서도 실질적인 유저 데이터가 있는 또 다른 서버에 요청해 사용자를 인증한다.
지금까지는 이 유저 데이터가 모두 서버에 있었다. 가입할 때 기입했던 아이디와 패스워드를 제대로 입력하면, 서버에서 데이터베이스를 조회해 일치하는 지 확인하고 유저를 판별해냈다.

new auth

하지만 요근래 인증하는 방식이 또 변화가 있었다. 특히 인상 깊은 것은 생체인식 방식인데, 생체인식은 단말기에서 인증에 대한 검증이 이루어지고 그 결과를 서버에 보내는 방식으로 이루어진다. 필자는 실질적으로 클라이언트에서 검증을 하는 방식을 생체인식으로 처음 확인했다.

Web3의 지갑을 이용한 인증도 이와 비슷하다. 유저가 지갑을 통해서 본인임을 인증하려면 임의의 데이터를 서명해서 서버에 보내고, 해독한 결과 본인의 계정(Public Key)이 나오는 것을 확인시키면 된다. 즉 클라이언트가 인증하는 데 필요한 주요한 요소인 Private Key를 갖고 있기 때문에, 인증 과정에서 헤게모니를 갖게 된 것이다.
이는 영지식증명을 응용한 방법이다. 간단히 영지식증명이란 사실을 그대로 드러내지 않고, 우회해서 증명하는 방식이다. 장 자크 키스케다라는 사람은 영지식증명을 설명하기 위해 알리바바의 동굴이라는 예시를 들어 설명한 바 있다. 이를 통해 Private Key를 직접 드러내지 않고도, Public Key와 쌍이 되는 Private Key를 소유하고 있는 본인임을 증명할 수 있다.

인증과정

replay attack

그렇다면 Web3에서 본인을 인증하려면 Private Key로 서명한 데이터를 서버에 보내는 것이 다일까? 이 서명된 데이터와 서명에 사용된 메시지만 있으면 Public Key를 추출할 수 있다. 하지만 여기서 문제가 있다. 만약 서명된 데이터가 서버로 보내지는 통신 과정 중 누군가에게 노출이 된다면, 다른 누군가가 이 데이터를 서버에 보내어 또 인증을 시도할 수 있다. 그렇다면 Private Key를 가지고 있지 않아도 누군가가 나 대신에 인증에 성공해 크리티컬한 권한을 획득할 수도 있다.

이 때문에 아직까지는 강조하지 않았던 서명하고자 하는 메시지의 역할이 대두된다. 지금까지 메시지는 고정되어 전송됨을 가정했다. 하지만 이 메시지는 임의로 수정되어야 한다. 서명이라는 행위는 Private Key와 메시지의 해시값을 얻어내는 것이라고 생각하자. 해시값은 소스가 조금이라도 수정이 되면 전체 값이 크게 변동이 되는 특징을 가지고 있다. 메시지가 수정이 되면 당연히 해시값도 변경된다.

이 때문에 한 번 사용된 메시지는 다음에 또 사용되지 않게 nonce값을 가지는 경우도 있다. nonce 값은 각 유저에게 귀속되는 프로퍼티고, 메시지가 요청될 때마다 1씩 증가한다. 사실 nonce 대신 타임스탬프를 찍어 보내줘도 역할은 거의 수행되는 것 같다.

그래서 인증하는 첫 과정에서 클라이언트는 서버 측에 유저가 서명해야 할 메시지를 요청하게 된다.

서버에서는 위에서 설명한 것처럼 해당 유저에게 보냈던 서명해야할 메시지를 보관하고 있고, 클라이언트에서 서명해서 보낸 데이터를 전송받는다. 이 서명된 데이터를 보관하고 있던 메시지와 함께 복호화를 진행하면 Public Key가 추출된다. 이 Public Key가 유저가 현재 클라이언트에서 사용하고 있는 계정의 Public Key와 맞는지 확인한 뒤, JWT나 세션을 발급해서 인증 상태를 유지시킨다.

정리해보자면 전 Web3의 전 인증과정은 다음과 같다.

  1. 로그인 시도를 위해 클라이언트가 서버에 서명할 메시지 요청
  2. 서버는 nonce와 타임스탬프, 필요한 정보들과 함께 메시지를 만들어 클라이언트에게 전송
  3. 클라이언트 지갑에 있는 Private Key로 메시지 서명 및 서버에 서명 데이터 전송
  4. 서명된 데이터와 메시지 데이터를 통해 복호화를 시도
  5. Public Key 확인 이후 클라이언트에 JWT 혹은 세션을 발급해 인증 상태 유지

process web3 auth

정리

Web3의 주요한 개념이 익명성이란 것이 있다. 그리고 데이터 주권의 이전이라는 개념도 존재한다. 영지식증명의 하나인 공개키-개인키 방식을 통해, 본인이라는 것을 증명할 수 있는 새로운 방법이 생겼다. 이는 익명성을 보장하면서도, 서버에서 갖고있던 중요한 데이터를 클라이언트가 소유할 수 있게 영향을 미쳤다. 그만큼 클라이언트도 Private Key를 보관하는 것에 더 신중해질 필요가 있는 것 같다. Web3를 십분 이해하고, Web3다운 서비스 구성을 계속해서 고민해보려고 한다.

profile
블록체인 개발 공부 중입니다, 프로그래밍 공부합시다!

2개의 댓글

comment-user-thumbnail
2023년 7월 17일

저도 개발자인데 같이 교류 많이 해봐요 ㅎㅎ! 서로 화이팅합시다!

1개의 답글