page-icon
개발
jsonwebtoken ^9.0.0에서 sign function Error
sooros5132-avatarsooros513212/23/2022JWT
리액트(프론트엔드)에서 기존에 사용하던 ^8.5.1이 높은 보안이슈가 있길래 최신으로 업데이트 했더니 sign이 안되는 현상이 발견됐다…
typescript
Unhandled Runtime Error
TypeError: Right-hand side of 'instanceof' is not an object
해당 보안이슈 메시지
bash
# npm audit report

jsonwebtoken  <=8.5.1
Severity: high
jsonwebtoken has insecure input validation in jwt.verify function - https://github.com/advisories/GHSA-27h2-hvpr-p74q
jsonwebtoken's insecure implementation of key retrieval function could lead to Forgeable Public/Private Tokens from RSA to HMAC - https://github.com/advisories/GHSA-hjrf-2m68-5959
jsonwebtoken vulnerable to signature validation bypass due to insecure default algorithm in jwt.verify() - https://github.com/advisories/GHSA-qwph-4952-7xr6
jsonwebtoken unrestricted key type could lead to legacy keys usage  - https://github.com/advisories/GHSA-8cf7-32gw-wr33
fix available via `npm audit fix --force`
Will install jsonwebtoken@9.0.0, which is a breaking change
node_modules/jsonwebtoken

1 high severity vulnerability
gzipped만 봐도 용량이 커보이긴 한다.
node의 내장 crypto 모듈을 이용해서 sign, base64UrlFromBase64 구현
typescript
import crypto from 'crypto';

export function base64UrlFromBase64(str: string) {
  return str.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}

export function sign(payload: any, secretKey: string) {
  const header = {
    alg: 'HS256',
    typ: 'JWT'
  };
  const encodedHeader = base64UrlFromBase64(Buffer.from(JSON.stringify(header)).toString('base64'));

  const encodedPayload = base64UrlFromBase64(
    Buffer.from(JSON.stringify(payload)).toString('base64')
  );

  const signature = base64UrlFromBase64(
    crypto
      .createHmac('sha256', secretKey)
      .update(encodedHeader + '.' + encodedPayload)
      .digest('base64')
  );

  return `${encodedHeader}.${encodedPayload}.${signature}`;
}
💡
base64url란? base64는 인코딩 할 때 url에서 사용하는 +, / 이 2개가 포함되어 있어서 안전하지 않으니 -, _로 대체한 방식이다. 추가로 base64에서는 6 bit로 떨어지지 않으면 마지막에 =문자를 추가하는데 = 문자도 제거해서 안전하게 만들면 base64url 완성
  1. header, payload는 base64url 인코딩, signature은 node의 crypto 라이브러리를 이용해서 sha256인코딩을 한다.
  1. header, payload, signature.으로 이어주면 JWT완성
https://jwt.io/에서 간단히 Verify테스트해볼 수 있다.
  1. Signature 부분에 키를 넣는다.
  1. Encoded 부분에 sign에서 나온 문자열을 넣는다.
Signature Verified가 뜨면 정상적으로 동작한다고 보면 됨.
jwt.io에서 모듈을 찾아 설치할까 했지만 sign만 썼던걸 생각하면 필요할까 싶어서 JWT에 대해 찾아보니 생각보다 간단하게 작동하길래 그냥 구현하게 됐다.
jsonwebtoken모듈 삭제로 인해 빌드해보면 16kb정도 줄었다.
Buffer객체에 toStringbase64url이 있긴 한데 처음엔 됐다가 안되던데 왜 그런진 모르겠다.