nonce[OAuth]

SnowCat·2023년 9월 14일
0
post-thumbnail

문제상황

  • 프론트엔드에서 소셜 연동 기능을 구현하던 중 썸네일과 같은 오류를 발견했다.
  • nonce값을 추가하라 하던데, nonce값이 정확히 무엇을 의미하는가 궁금해서 정리해둔다.

리플레이 공격이란?

  • 해커가 기존 사용자의 유용한 데이터 전송을 가로채 실제 사용자인 척 위장하는 방법
  • 프론트엔드에서 직접 id 토큰을 가져오도록 설정한 경우, 구글로그인 이후 다른 페이지로 리다이렉트 될 때 평문으로 토큰값이 전달되기 때문에 주소만 가로채면 다른 사용자인 척 할 수 있음
  • 이를 방지하기 위해 공격자가 가로챌 수 없는 임의의 데이터를 생성해 통신에 사용해줘야 하며 OAuth인증을 사용할 때에는 nonce값을 전달하게 됨

예시 코드

  • Google에 Oauth 토큰을 받아와 기존에 cognito에 등록된 사용자에 연동하고 싶다. cognito에서 adminlinkproviderforuser를 사용하기 위해서는 서버에 id토큰을 제공해야 한다.
  • 우선 프론트엔드에서는 로그인 시도시 임의의 nonce값을 생성해 localStorage에 저장해준다.
const onLogin = () => {
  const nonce = uuid();
  window.localStorage.set("nonce", nonce);
  window.location.href =  `https://accounts.google.com/o/oauth2/v2/auth?` +
              `identity_provider=Google&` +
              `redirect_uri=${process.env.WEB_DOMAIN}/auth/link/google&` +
              "response_type=id_token token&" +
              `client_id=${process.env.CLIENT_ID}&` +
              "scope=email openid profile&" +
              `nonce=${nonce}`,
}
  • 로그인 이후 로컬스토리지에 있는 nonce값을 백엔드 서버에 같이 전송해준다.
const parsedHash = new URLSearchParams(window.location.hash.substring(1));
const idToken = parsedHash.get("id_token");
const tokenType = parsedHash.get("token_type");

await fetch("몰루", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  	"Authorization": "Bearer tokenText"
  },
  body: {
    idToken: `${tokenType} ${idToken}`,
    nonce: window.localStorage.get("nonce")
  }
}
  • 백엔드에서는 받은 id토큰과 nonce값이 같은지를 검증해준다.
const { idToken, nonce: clientNonce } = await request.json();
const { sub, nonce: tokenNonce } = await parseJwt(idToken.split(" ")[1]);

// Nonce value error
if (clientNonce !== tokenNonce) {
  return NextResponse.json({ error: "Replay Attack" }, { status: 403 });
}

출처:
https://academy.gopax.co.kr/ripeulrei-gonggyeogiran-mueosingayo/
https://developers.google.com/identity/openid-connect/openid-connect#appsetup

profile
냐아아아아아아아아앙

0개의 댓글