๐ŸฆŠFoxulator
๐Ÿ”

JWT ๋งŒ๋ฃŒ์‹œ๊ฐ„ ๊ณ„์‚ฐ๊ธฐ

JWT ํ† ํฐ์„ ๋””์ฝ”๋”ฉํ•˜์—ฌ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ํ™•์ธํ•˜๊ณ , ์ƒˆ ํ† ํฐ์˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๊ฒ€์ฆ ์—†์ด ํด๋ผ์ด์–ธํŠธ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

JWT(JSON Web Token) ๋งŒ๋ฃŒ์‹œ๊ฐ„ ๊ณ„์‚ฐ๊ธฐ๋Š” ํ† ํฐ์˜ `exp` claim์— ๋“ค์–ด๊ฐˆ Unix timestamp๋ฅผ ๋น ๋ฅด๊ฒŒ ์‚ฐ์ถœํ•˜๋Š” ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. 1์‹œ๊ฐ„ ํ›„, 7์ผ ํ›„, 30์ผ ํ›„ ๋“ฑ ์ž์ฃผ ์“ฐ์ด๋Š” ๋งŒ๋ฃŒ ์‹œ์ ์„ ํด๋ฆญ ํ•œ ๋ฒˆ์œผ๋กœ ๊ณ„์‚ฐํ•˜๊ณ , ISO 8601 ํ˜•์‹๊ณผ ํ˜„์žฌ ์‹œ๊ฐ๋„ ํ•จ๊ป˜ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. NextAuth, Passport, Auth0, AWS Cognito, Firebase Auth ๋“ฑ ์–ด๋–ค ์ธ์ฆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“  ์ ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

โš ๏ธ ์ด ๋„๊ตฌ๋Š” ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฒ€์ฆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณด์•ˆ์ด ํ•„์š”ํ•œ ๊ฒ€์ฆ์€ ๋ฐ˜๋“œ์‹œ ์„œ๋ฒ„์—์„œ ์ˆ˜ํ–‰ํ•˜์„ธ์š”.

๐Ÿ“– ์‚ฌ์šฉ๋ฒ•

  1. JWT ํ† ํฐ์„ ์ž…๋ ฅ๋ž€์— ๋ถ™์—ฌ๋„ฃ์œผ์„ธ์š”
  2. ์ž๋™์œผ๋กœ Header, Payload๊ฐ€ ๋””์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค
  3. ๋งŒ๋ฃŒ ์‹œ๊ฐ„๊ณผ ๋‚จ์€ ์‹œ๊ฐ„์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค
  4. ์ƒ์„ฑ ํƒญ์—์„œ ์ƒˆ ํ† ํฐ์˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

โœจ ์ฃผ์š” ๊ธฐ๋Šฅ

  • โœ“JWT ํ† ํฐ ์ž๋™ ๋””์ฝ”๋”ฉ (Header, Payload)
  • โœ“๋งŒ๋ฃŒ ์‹œ๊ฐ„(exp) ๋ฐ ๋ฐœ๊ธ‰ ์‹œ๊ฐ„(iat) ํ™•์ธ
  • โœ“์‹ค์‹œ๊ฐ„ ์นด์šดํŠธ๋‹ค์šด์œผ๋กœ ๋‚จ์€ ์‹œ๊ฐ„ ํ‘œ์‹œ
  • โœ“๋งŒ๋ฃŒ ์—ฌ๋ถ€ ์ฆ‰์‹œ ํ™•์ธ
  • โœ“ํ† ํฐ ์ƒ์„ฑ ์‹œ ํ•„์š”ํ•œ ์‹œ๊ฐ„ ํด๋ ˆ์ž„ ๊ณ„์‚ฐ
  • โœ“๋ถ„/์‹œ๊ฐ„/์ผ ๋‹จ์œ„ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์„ค์ •
  • โœ“nbf(Not Before) ํด๋ ˆ์ž„ ์„ ํƒ ์ถ”๊ฐ€
  • โœ“JSON ํ˜•์‹์œผ๋กœ ํด๋ ˆ์ž„ ๋ณต์‚ฌ ๊ฐ€๋Šฅ

๐Ÿ“ ๊ณ„์‚ฐ ๊ณต์‹

exp = iat + ์œ ํšจ๊ธฐ๊ฐ„(์ดˆ), iat = ํ˜„์žฌ Unix ํƒ€์ž„์Šคํƒฌํ”„

๐Ÿ’ก ๊ณ„์‚ฐ ์›๋ฆฌ

  • โ€ขJWT(JSON Web Token)๋Š” ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค: Header.Payload.Signature
  • โ€ขHeader์—๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜(alg)๊ณผ ํ† ํฐ ํƒ€์ž…(typ)์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  • โ€ขPayload์—๋Š” ํด๋ ˆ์ž„(claims)์ด ํฌํ•จ๋˜๋ฉฐ, iat/exp/nbf๋Š” ์‹œ๊ฐ„ ๊ด€๋ จ ํ‘œ์ค€ ํด๋ ˆ์ž„์ž…๋‹ˆ๋‹ค.
  • โ€ขiat(Issued At): ํ† ํฐ์ด ๋ฐœ๊ธ‰๋œ ์‹œ๊ฐ„ (Unix ํƒ€์ž„์Šคํƒฌํ”„)
  • โ€ขexp(Expiration Time): ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜๋Š” ์‹œ๊ฐ„ (Unix ํƒ€์ž„์Šคํƒฌํ”„)
  • โ€ขnbf(Not Before): ํ† ํฐ์ด ์œ ํšจํ•ด์ง€๋Š” ์‹œ์ž‘ ์‹œ๊ฐ„ (Unix ํƒ€์ž„์Šคํƒฌํ”„)
  • โ€ข์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ์„œ๋ฒ„์˜ ๋น„๋ฐ€ํ‚ค๋กœ ์ƒ์„ฑ๋˜๋ฏ€๋กœ, ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ๊ฒ€์ฆ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Š ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

Access Token 1์‹œ๊ฐ„ ํ›„ ๋งŒ๋ฃŒ

Math.floor(Date.now() / 1000) + 3600

โ†’ 1747645200 (์˜ˆ์‹œ, ํ˜„์žฌ ์‹œ๊ฐ ๊ธฐ์ค€ +3600์ดˆ)

Refresh Token 30์ผ ํ›„ ๋งŒ๋ฃŒ

Math.floor(Date.now() / 1000) + 30 * 86400

โ†’ ํ˜„์žฌ + 2,592,000์ดˆ (30์ผ)

๊ธฐ์กด ํ† ํฐ ๋””๋ฒ„๊น…: exp 1731715200 โ†’ ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํ˜•์‹

new Date(1731715200 * 1000).toISOString()

โ†’ 2024-11-16T00:00:00.000Z (UTC)

๐ŸŽฏ ํ™œ์šฉ ์‚ฌ๋ก€

  • โ–ธJWT ๋ฐœ๊ธ‰ ์‹œ ์ ์ ˆํ•œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ (Access 15๋ถ„~1์‹œ๊ฐ„, Refresh 7์ผ~30์ผ) ์„ค๊ณ„
  • โ–ธ๊ธฐ์กด ํ† ํฐ ๋””๋ฒ„๊น… ์‹œ exp ๊ฐ’์ด ์–ธ์ œ์ธ์ง€ ์‚ฌ๋žŒ ๋ˆˆ์œผ๋กœ ํ™•์ธ
  • โ–ธOAuth 2.0 / OIDC ํ† ํฐ ์ˆ˜๋ช… ์ •์ฑ… ์ˆ˜๋ฆฝ
  • โ–ธํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ๋งŒ๋ฃŒ ์ž„๋ฐ•/๋งŒ๋ฃŒ๋œ ํ† ํฐ ์ƒ์„ฑํ•˜์—ฌ ๊ฐฑ์‹  ๋กœ์ง ๊ฒ€์ฆ
  • โ–ธ๋‹ค๊ตญ๊ฐ€ ์„œ๋น„์Šค์˜ ์‹œ๊ฐ„๋Œ€ ๋ฌด๊ด€ํ•œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์ผ๊ด€์„ฑ ๋ณด์žฅ

โš ๏ธ ํ”ํ•œ ์‹ค์ˆ˜์™€ ์ฃผ์˜์‚ฌํ•ญ

์‹ค์ˆ˜: ์ดˆ(seconds) ๋Œ€์‹  ๋ฐ€๋ฆฌ์ดˆ(milliseconds)๋ฅผ ๊ทธ๋Œ€๋กœ exp์— ๋„ฃ์–ด ํ† ํฐ์ด ๋ฏธ๋ž˜ ๋งŒ๋ฃŒ๋กœ ์žกํž˜

๋ฐ”๋ฅด๊ฒŒ: JWT์˜ exp๋Š” Unix timestamp ์ดˆ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. `Date.now() / 1000` ์œผ๋กœ ๋ณ€ํ™˜ํ•˜์„ธ์š”. JavaScript์—์„œ๋Š” `Math.floor(Date.now() / 1000)` ๊ฐ€ ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ˆ˜: ๋กœ์ปฌ ์‹œ๊ฐ„๋Œ€ ๊ธฐ์ค€์œผ๋กœ exp ๊ณ„์‚ฐํ•ด์„œ ๋‹ค๋ฅธ ์ง€์—ญ ์‚ฌ์šฉ์ž์˜ ํ† ํฐ์ด ์ผ์ฐ/๋Šฆ๊ฒŒ ๋งŒ๋ฃŒ

๋ฐ”๋ฅด๊ฒŒ: exp๋Š” UTC ๊ธฐ์ค€ ์ดˆ ๋‹จ์œ„ timestamp์ด๋ฏ€๋กœ ์‹œ๊ฐ„๋Œ€์™€ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ, ๋ฐœ๊ธ‰ ์„œ๋ฒ„์™€ ๊ฒ€์ฆ ์„œ๋ฒ„์˜ ์‹œ๊ณ„ ๋™๊ธฐํ™”(NTP)๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

์‹ค์ˆ˜: ๋„ˆ๋ฌด ๊ธด Access Token ๋งŒ๋ฃŒ(์˜ˆ: 30์ผ)๋กœ ๋ณด์•ˆ ์œ„ํ—˜ ๋…ธ์ถœ

๋ฐ”๋ฅด๊ฒŒ: Access Token์€ 15๋ถ„~1์‹œ๊ฐ„ ๊ถŒ์žฅ. Refresh Token์œผ๋กœ ๊ฐฑ์‹ ํ•˜๋Š” ํŒจํ„ด์ด ํ‘œ์ค€์ž…๋‹ˆ๋‹ค. Refresh Token ํƒˆ์ทจ ๋Œ€๋น„ rotation์„ ํ•จ๊ป˜ ์ ์šฉํ•˜์„ธ์š”.

์‹ค์ˆ˜: Clock skew(์„œ๋ฒ„๊ฐ„ ์‹œ๊ณ„ ์ฐจ์ด)๋กœ ์œ ํšจํ•œ ํ† ํฐ์ด invalid ์ฒ˜๋ฆฌ๋จ

๋ฐ”๋ฅด๊ฒŒ: JWT ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ `clockTolerance` ์˜ต์…˜(๊ธฐ๋ณธ 0์ดˆ)์„ 30~60์ดˆ๋กœ ์„ค์ •ํ•˜์—ฌ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์˜ ์‹œ๊ณ„ ์˜ค์ฐจ๋ฅผ ํก์ˆ˜ํ•˜์„ธ์š”.

โ“ ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ

Q. ์™œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฒ€์ฆํ•˜์ง€ ์•Š๋‚˜์š”?

A. ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๊ฒ€์ฆ์—๋Š” ์„œ๋ฒ„์˜ ๋น„๋ฐ€ํ‚ค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์—์„œ ๋น„๋ฐ€ํ‚ค๋ฅผ ๋…ธ์ถœํ•˜๋ฉด ๋ณด์•ˆ ์œ„ํ—˜์ด ์žˆ์œผ๋ฏ€๋กœ, ์‹ค์ œ ๊ฒ€์ฆ์€ ์„œ๋ฒ„์—์„œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q. ๋งŒ๋ฃŒ๋œ ํ† ํฐ๋„ ๋””์ฝ”๋”ฉ๋˜๋‚˜์š”?

A. ๋„ค, ๋งŒ๋ฃŒ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด ๋””์ฝ”๋”ฉ์€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋งŒ๋ฃŒ ์ƒํƒœ๊ฐ€ ๋นจ๊ฐ„์ƒ‰์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

Q. iat, exp, nbf๊ฐ€ ์—†๋Š” ํ† ํฐ๋„ ์žˆ๋‚˜์š”?

A. ๋„ค, ์ด ํด๋ ˆ์ž„๋“ค์€ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ๋งŒ๋ฃŒ ์‹œ๊ฐ„์ด ์—†๋Š” ํ† ํฐ์€ ์˜๊ตฌ์ ์œผ๋กœ ์œ ํšจํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ณด์•ˆ์ƒ ๊ถŒ์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Q. ์ƒ์„ฑ๋œ ํด๋ ˆ์ž„์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋‚˜์š”?

A. ๋ณต์‚ฌํ•œ JSON์„ ์„œ๋ฒ„์˜ JWT ์ƒ์„ฑ ๋กœ์ง์—์„œ payload์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์„œ๋ช…์€ ์„œ๋ฒ„์—์„œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q. ์–ด๋–ค ๋งŒ๋ฃŒ ์‹œ๊ฐ„์ด ์ ๋‹นํ•œ๊ฐ€์š”?

A. ์šฉ๋„์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. Access Token์€ 15๋ถ„~1์‹œ๊ฐ„, Refresh Token์€ 7~30์ผ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ๋ฏผ๊ฐํ•œ ์ž‘์—…์—๋Š” ์งง์€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”— ๊ด€๋ จ ๊ณ„์‚ฐ๊ธฐ

์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค.

JWT ๋งŒ๋ฃŒ์‹œ๊ฐ„ ๊ณ„์‚ฐ๊ธฐ | Foxulator