반응형

SIWE (Sign In With Ethereum)


SIWE 는 이더리움 블록체인 기반의 사용자 인증 방식. 사용자는 자신의 이더리움 지갑을 통해 웹 서비스나 앱에 로그인할 수 있으며, 이는 기존의 중앙화된 아이디 제공자 (IDP) 대신 자신이 소유한 암호화폐 지갑으로 신원을 증명하는 방식이다.

최근에 회사의 Dapp 에 SIWE 인증 서비스를 도입했다. SIWE 를 도입하면서 백엔드 개발자 분들과 약간의 논의가 있었는데 ‘인증 안해도 로그인 되는데 굳이 로그인을 해야하나?’ 라는 이유였다. 나는 여기서 제대로 답을 하지 못해 리서치 겸 글을 작성해본다.

Web2 에서의 인증


1. 비밀번호 기반 인증 (ID/PW)

  • 사용자가 아이디와 비밀번호를 입력하면, 서버가 이를 데이터베이스와 대조하여 신원을 확인합니다.
  • 가장 기본적이고 널리 사용되는 방식이지만, 비밀번호 유출·재사용 등으로 인한 보안 취약점이 존재합니다.

2. 세션 기반 인증

  • 로그인 성공 시 서버가 세션을 생성하고, 세션 ID를 쿠키에 저장하여 클라이언트에 전달합니다.
  • 사용자가 요청할 때마다 쿠키에 담긴 세션 ID로 인증을 수행합니다.
  • 세션 정보는 서버에 저장되며, 서버 확장 시 세션 동기화가 필요합니다.

3. 토큰 기반 인증 (JWT 등)

  • 사용자가 로그인하면 서버가 JWT(JSON Web Token) 등 서명된 토큰을 발급합니다.
  • 클라이언트는 이후 요청에 이 토큰을 포함시켜 서버에 제출하며, 서버는 토큰의 유효성을 검증해 인증을 처리합니다.
  • 토큰은 클라이언트가 직접 저장·관리하며, 서버는 상태를 유지하지 않아 확장성이 높습니다.

4. OAuth 2.0 (소셜 로그인 등)

  • Google, Facebook, Kakao 등 외부 서비스 계정으로 로그인할 수 있게 해주는 표준 프로토콜입니다.
  • 대표적인 인증 방식:
    • Authorization Code Grant: 서버 사이드에서 인증, 보안성이 높아 가장 많이 사용됨
    • Implicit Grant: 클라이언트(브라우저)에서 직접 토큰을 받는 방식, 보안상 현재는 거의 사용하지 않음
    • Resource Owner Password Credentials Grant: 신뢰 관계가 있는 앱에서만 사용
    • Client Credentials Grant: 서버-서버 간 인증.
  • 사용자는 외부 서비스의 로그인 페이지에서 인증을 마치고, 서비스는 액세스 토큰을 받아 인증에 활용합니다.

5. 2단계 인증(2FA), 생체 인증, 인증서 기반 인증

  • 2단계 인증(2FA): 비밀번호 외에 일회용 코드(OTP), SMS, 이메일 인증 등 추가 인증을 요구.
  • 생체 인증: 지문, 얼굴 인식 등으로 인증.
  • 인증서 기반 인증: 공인인증서, 사설 인증서 등 디지털 인증서를 활용.

이처럼 Web2에서는 비밀번호, 세션, 토큰, OAuth, 2FA 등 다양한 인증 방식이 사용되며, 서비스의 특성과 보안 요구 수준에 따라 여러 방식을 조합해 적용하는 것이 일반적입니다.

Web3 에서의 SIWE 인증


  1. 라이브러리 혹은 지갑 연결
    • 라이브러리에서 제공하는 연결 혹은 injected 된 지갑을 통해 로그인
  2. SIWE 서명 프로세스 시작
  3. 서버에서 nonce , message, expired, 로그인 목적 등 생성 후 전송
  4. SIWE message 생성 후 사용자에게 출력
  5. 메시지 서명
  6. service.org wants you to sign in with your Ethereum account: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 I accept the ServiceOrg Terms of Service: https://service.org/tos URI: https://service.org/login Version: 1 Chain ID: 1 Nonce: 32891756 Issued At: 2021-09-30T16:25:24Z Resources: - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ - https://example.com/my-web2-claim.json
  7. 서명 데이터 서버에 전달
  8. 서명 검증
  9. 인증 완료 및 세션 발급

 

이 프로세스만 봐서는 SIWE 를 써야하는 이유를 모를 수 있다. 쓰지 않았을 때의 문제점을 알아보자.

Why SIWE?


  1. 지갑 소유권 검증 부재
    • 단순히 지갑 주소 제출 및 연결하는 방식은 해당 주소의 실제 소유자를 검증 못함.
    • 누군가 공개된 이더리움 주소를 임의로 입력하거나, 다른 사용자의 주소를 도용해 서비스에 접근할 수 있음.
    • 서버에서는 사용자가 누구인지 확인할 방법이 없어, 주소만 알면 누구나 그 사용자인 척할 수 있는 심각한 보안 취약점.
  2. 세션 하이재킹 및 신원 도용
    • SIWE 는 서명 메시지에 nonce (임의의 난수) 와 세션 정보를 포함시켜, 재사용 공격이나 세션 하이재킹을 방지
      • 재사용 공격(Relay Attack)
        • 이미 생성된 서명을 재사용해 인증을 우회
      • 세션 하이재킹
        • nonce 관리가 제대로 이뤄지지 않아, 사용자가 로그아웃해도 동일한 서명 메시지로 재로그인이 가능
    • SIWE 없이 단순히 주소를 전달할 경우, 이전에 사용된 정보가 그대로 재사용 가능
  3. 백엔드 API 보호 미흡
    • 백엔드 API 호출 시 신원 검증이 어려움
    • 누군가가 임의의 주소로 API 를 호출하면, 서비스는 잘못 인식할 수 있음.

다른 방법은 없나?


SIWE 말고도 사용자의 신원을 파악하는 블록체인 기술은 여러가지가 있다.

  1. DID (분산신원 인증)
    • W3C DID 표준을 기반, 블록체인에 등록된 분산 신원(DID) 과 검증가능 자격증명 (VC) 를 활용해 자신을 증명
    • 모바일 운전면허증
  2. NFT/토큰 소유 기반 인증
    • 특정 NFT , 토큰을 소유한 지갑만 서비스 접근 가능
  3. 다중 인증 (MFA)
    • 지갑 인증 외에 이메일, SMS, 생체 인증 등 추가인증 결합
  4. 소셜 로그인 연동형 Web3 인증
    • 소셜 로그인 시 생기는 지갑을 연결 및 인증
    • Wepin, Appkit 소셜 로그인
반응형

'BlockChain > Web3' 카테고리의 다른 글

[2] Web3 프론트엔드 연결 요약 (Wagmi + Reown)  (0) 2025.04.12
[1] Frontend in Web3  (0) 2025.04.06
반응형

리팩토링은 어려워


얼마 전 react-query 로 데이터 호출 프로세스를 변경했다. 이 때만해도 데이터가 술술 잘 불러와지더라니 낌새를 챘어야 했다. 컴포넌트를 하나 둘 추가하다 보니 Mypage 로 이동할 때마다 에러를 마주치게 되었다.

 

Should have a queue ~~

 

아니 이건 또 처음 보는 에런데,, 당황의 연속,,

구글링을 열심히 해보니 React Hook 의 규칙 중 하나인 “Hook 은 항상 동일한 순서로 호출되어야 한다” 는 규칙을 위반했을 때 발생한다고 한다. 그런데 이전까지는 잘되다가 갑자기 안되는 이유는 무엇?

범인은 바로 너


지피티가 알려준 나의 코드의 문제 부분을 함께 찾아보게 되었다. 예전의 나는 응애였기에 몰라 하란대로 한 부분이었는데 지금 와서 보니 너무 얼척이 없는 문제를 일으키고 있었다.. 공부의 중요성 ,,,,,,,

*// 문제가 있는 코드*

const purchaseUserIds = referralPurchases

.map(*purchase* => purchase?.user_id)

.filter(*id* => !!id);

*// 반복문 안에서 Hook을 호출하는 잘못된 패턴*

const userInfoQueries = purchaseUserIds.map(*userId* => useUserByUserId(userId));

 

문제 원인

useQuery 로 불러온 데이터를 순회하면서 또 useQuery 써버리기

정확히 말하면 반복문 내에서 Hook 을 호출하면 렌더링마다 Hook의 호출 순서가 달라질 수 있기 때문에 동적 Hook 호출이 되고 이는 리액트 규칙 위반이다.

React Hook 규칙


  1. 최상위에서만 호출
    1. 컴포넌트 혹은 커스텀 훅의 최상위 스코프에서만 훅을 호출
    2. 조건문, 반복문, 중첩된 함수 안에서 훅을 호출하면 안됨
  2. 렌더링마다 동일한 순서로 호출
    1. 컴포넌트가 매번 렌더링될 때 훅이 호출되는 순서와 개수가 변하면 안됨
    2. 호출 순서가 바뀌면 이전 렌더에서 저장해둔 “훅의 큐” 를 찾아올 수 없어 오류가 발생

왜 동일한 순서여야 할까?


리액트의 훅 동작 순서를 파악해보자.

  • 컴포넌트가 렌더링 될 때 마다, 리액트는 훅 호출 순서를 따라 hook1, hook2, … 순으로 큐를 꺼내 상태를 연결
  • 만약 어떤 렌더에서 hook2 가 호출되지 않거나, hook4 가 먼저 호출 되면 리액트는 이전에 저장된 큐가 없다고 판단하며 에러를 던짐

리액트가 훅 순서를 기억하는 이유 → 복작한 트리 구조를 따로 추적하지 않아도됨 → 단일 연결 리스트 형태로 훅 상태를 관리

해결 방법 (useQueries)


useQueries 는 훅 순서를 고정해 동적으로 쿼리를 생성할 수 있게 도와준다. 이 훅이 내가 원하는 기능을 구현하기에 안성맞춤이다고 느끼게 되었다.

import { useQueries } from '@tanstack/react-query';

*// 해결된 코드*

const userInfoQueries = useQueries({

queries: purchaseUserIds.map(userId => ({

queryKey: ['user', userId],

queryFn: () => useUserByUserId(userId),

enabled: Boolean(userId),

})),

});

오늘의 교훈


“Hook은 마법 같은 편의 기능이지만, 그 뒤에 숨은 내부 메커니즘을 이해해야 비로소 제대로 쓸 수 있다.”

— 익명의 React 개발자 —

반응형
반응형

오픈을 하루 앞두고 부담감이 솟구치지만 그래도 뭔가 잘 되어가고 있다는 기분에 그나마 버틸만 한 월요일 아침. 팀장님 왈 ‘NFT id 가 1001번 부터 url이 잘못되어있어요.’

 

예?!

아 엑셀 실수..


나는 보통 thirdweb에서 NFT 데이터를 관리하고 NFT를 민팅하는데 csv 파일을 사용해서 올린다. 이전에 했을때는 csv 파일에서 셀 우측하단을 잡고 주욱 잡아 늘리면 아래 행으로 복사가 되었다. 근데 여기서 이름에 해당하는 컬럼의 id 값만 바뀌고 image url은 안바뀌었는데 어찌된 영문인지 이번에는 url 도 같이 바뀌었다. 오마이갓

 

이래서 더블체킹을 해야하는데.. 여튼 자책할시간은 없다. 방법을 찾아보았다.

updateBaseUri


다행스럽게도 updateBaseUri 함수가 구현되어 있었다. nft 를 민팅하면 baseurl 을 기준으로 id 값별 url이 매핑된다. 그래서 tokenId = 1 이라면 baseUrl/1 이렇게 metadata가 매핑된다.

 

하지만, 나는 처음에 baseUrl 이 이미지만 바뀌는 구나! 나는 이미지만 바꾸면되니까 이걸로 고정시켜버리자! 해서 이미지 url을 붙여버렸더니 역시나 안됐다..

 

(이래서 선공부 후개발 해야하는데 급한대로 하다보니 이렇다.)

 

그래서 이걸 어떻게 해야하나 고민을 하다가 대박적인 아이디어가 떠올랐다.

그냥 다시 민팅해~


그렇다. 가장 간단하고 빠른 방법. 다시 민팅

 

일단 기존에 나는 두번의 배치 민팅을 통해 nft 를 배포했기 때문에 두개의 baseUrl 이 필요했다. 그래서 새로운 더미 컨트랙트를 배포하고 두번의 동일한 nft를 배포한 후 새로운 uri 를 적용시키기로 했다.

 

결과는 성공적.. 이렇게 내 오전 업무는 날라갔다.

 

오늘의 교훈

  • 더블체킹을 하자.
  • baseUrl 에 대해 알게 되었다.
반응형
반응형

이전 회차에서 rpc url 이 다운되어 새로운 rpc url을 fallback 형식으로 변경했다. 이는 보기엔 잘 되는것 처럼 보였으나,, 블록체인 비즈니스에 대해 잘 모르던 나에게 한 차례 위기가 찾아왔다.

나 사실 공짜아님 ㅋ


drpc 뭐시기 라는 url 을 사용하고 있던 와중 갑자기 408 에러 (맞나?) 가 뜨게 되어 에러 메시지를 보니 다음과 같았다.

??? : 프리티어 끝났다. 더 쓰려면 결제하던지 말던지 ㅋ

왜 공용이겠어


일단 운영서버를 돌려야하니까 급한대로 다시 polygon-main url 을 적용했다. 어느정도 해결이 되는 듯 했으나,, 저번부터 나를 괴롭히던 view 함수 호출 시 이전 값이 랜덤하게 불러와지는 현상이 다시 발생하게 되었다.

 

아 왜그래 진짜

 

이 오류를 해결하기 위해 지피티를 열심히 괴롭힌 결과, polygon-main url은 아마도 공용 url 이니까 여러개의 url을 fallback 했을 것 이고, 그렇기 때문에 가끔 이전 값을 보내줄 수 있다고 설명해줬다.. 고마워 지피티야..

 

이 오류를 안 이후 얼마되지 않아 바로 호출되었고 사태에 대해 설명했다. 솔루션에 대해 고민을 해야했고 찾아본 결과 두 가지로 추려졌다.

  1. 직접 노드를 구매해 관리하기
  2. Alchemy, Infura 등 노드 관리 서비스 이용하기

메인 체인이 확정되지 않았기 때문에 노드를 구매한다는 것 자체가 리스키 했기 때문에 금방 사설 노드를 사용하는것으로 결정 되었다.

새로운 서비스는 어려워


https://www.alchemy.com/

 

Alchemy - the web3 development platform

Whether you're a beginner developer, startup, web3 market leader, or a large enterprise, Alchemy makes multichain web3 development easy.

www.alchemy.com

 

우리 서비스는 rpc call 이 많지 않기때문에 요금제를 크게 잡을 필요가 없어 프리티어 윗단계로 진행했다. 대시보드를 생성하고나니 Alchemy 도메인과 API key를 받게 되었고, 이를 합쳐서 wagmi 에 적용만 해준다면 손쉽게 적용할 수 있었다.

 

이제 새로고침을 한 후 rpc call 이 얼마나 사용되나 볼까? 했더니 얼래 ? 새로고침했다고 call 이 90번이나 호출된 것이다. 도대체 어디서,,

 

지금은 당장 개발이 급해 최적화를 할 시간은 없지만 언젠가는 해결해야할 문제가 생겼다.. 야호

 

여튼 rpc 가 여러모로 나를 많이 괴롭혔다. 여기서 얻은 교훈. 어떻게든 돈을 벌 수 있다(?) 는 아니고 너무 공짜 좋아하지 말자 입니다.

반응형

+ Recent posts