Cookie์™€ ๋กœ๊ทธ์ธ ๐Ÿช(HttpOnly์™€ Secure Cookie ๊ทธ๋ฆฌ๊ณ  withCredentials)

yiwoojungยท2023๋…„ 8์›” 26์ผ
15

๋„คํŠธ์›Œํฌ

๋ชฉ๋ก ๋ณด๊ธฐ
1/2
post-thumbnail

Cookie๋ž€? ๐Ÿช

์ฟ ํ‚ค๋ž€ ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์›น ๋ธŒ๋ผ์šฐ์ €์— ์ „์†กํ•˜๋Š” ์ž‘์€ ๋ฐ์ดํ„ฐ ์กฐ๊ฐ ์ด๋‹ค.

  • ๋ธŒ๋ผ์šฐ์ €๋Š” ๊ทธ ๋ฐ์ดํ„ฐ ์กฐ๊ฐ๋“ค์„ ์ €์žฅํ•ด ๋†“์•˜๋‹ค๊ฐ€, ๋™์ผํ•œ ์„œ๋ฒ„์— ์žฌ์š”์ฒญ ์‹œ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๊ป˜ ์ „์†กํ•œ๋‹ค.
  • ์ฟ ํ‚ค๋Š” ๋‘ ์š”์ฒญ์ด ๋™์ผํ•œ ๋ธŒ๋Ÿฌ์šฐ์ €์—์„œ ๋“ค์–ด์™”๋Š”์ง€ ์•„๋‹Œ์ง€๋ฅผ ํŒ๋‹จํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์ด๋ฅผ ์ด์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ์ƒํƒœ๊ฐ€ ์—†๋Š” http ํ”„๋กœํ† ์ฝœ์—์„œ ์ƒํƒœ ์ •๋ณด๋ฅผ ๊ธฐ์–ต์‹œ์ผœ ์ฃผ๊ธฐ ๋•Œ๋ฌธ.

์ฟ ํ‚ค๋ฅผ ์–ธ์ œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„๊นŒ

  1. ์„ธ์…˜ ๊ด€๋ฆฌ
    • ์„œ๋ฒ„์— ์ €์žฅํ•ด์•ผ ํ•  ๋กœ๊ทธ์ธ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋“ฑ์˜ ์ •๋ณด ๊ด€๋ฆฌ
  2. ๊ฐœ์ธํ™”
    • ์‚ฌ์šฉ์ž ์„ ํ˜ธ, ํ…Œ๋งˆ ๋“ฑ์˜ ์„ธํŒ…
  3. ํŠธ๋ž˜ํ‚น
    • ์‚ฌ์šฉ์ž์˜ ํ–‰๋™์„ ๊ธฐ๋กํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ์šฉ๋„

์ฟ ํ‚ค๋Š” ์–ด๋–ค ํŠน์ง•์ด ์žˆ์„๊นŒ?

  1. http ํ†ต์‹ ์„ ํ•œ๋‹ค๋ฉด ์ฟ ํ‚ค๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
  2. ๊ฐ™์€ ๋„๋ฉ”์ธ์ด๋ผ๋ฉด header ์— ์ž๋™์œผ๋กœ ์ฟ ํ‚ค๊ฐ€ ๋‹ด๊ฒจ์„œ ๋ณด๋‚ด์ง„๋‹ค.
    • ๊ฐ™์€ ๋„๋ฉ”์ธ์ด๋ผ๋ฉด ์„œ๋กœ ๋‹ค๋ฅธ scheme์ผ์ง€๋ผ๋„ ์ฟ ํ‚ค๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  3. ์„œ๋ฒ„์—์„œ๋„, ํด๋ผ์ด์–ธํŠธ์—์„œ๋„ ์ƒ์„ฑ/์ ‘๊ทผ/๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  4. http์™€ https ์‚ฌ์ด์—๋„ ์ฟ ํ‚ค๋ฅผ ๊ตํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • secure ์ ‘๋ฏธ์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด https ์—์„œ๋งŒ ์ฟ ํ‚ค๋ฅผ ์ „์†กํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฟ ํ‚ค๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž!

  • HTTP ์š”์ฒญ์„ ์ˆ˜์‹ ํ•  ๋•Œ, ์„œ๋ฒ„๋Š” ์‘๋‹ต๊ณผ ํ•จ๊ป˜ Set-Cookie header ๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ฟ ํ‚ค๋Š” ๋ณดํ†ต ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ์ €์žฅ๋œ๋‹ค.
  • ์ฟ ํ‚ค๋Š” ๊ฐ™์€ ์„œ๋ฒ„์— ์˜ํ•ด ๋งŒ๋“ค์–ด์ง„ ์š”์ฒญ๋“ค์˜ Cookie HTTP header ์•ˆ์— ํฌํ•จ๋˜์–ด ์ „์†ก๋œ๋‹ค.
  • ๋งŒ๋ฃŒ์ผ๊ณผ ์ง€์†์‹œ๊ฐ„์„ ๋ช…์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋งŒ๋ฃŒ๋œ ์ฟ ํ‚ค๋Š” ๋” ์ด์ƒ ๋ณด๋‚ด์ง€์ง€ ์•Š๋Š”๋‹ค.
  • ํŠน์ • ๋„๋ฉ”์ธ ํ˜น์€ ๊ฒฝ๋กœ๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์ฟ ํ‚ค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •๋  ์ˆ˜ ์žˆ๋‹ค.

Set-Cookie: <cookie-name>=<cookie-value>

์ด ์„œ๋ฒ„ ํ—ค๋”๋Š” ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•˜๋ผ๊ณ  ์ „๋‹ฌํ•œ๋‹ค.

# ์„œ๋ฒ„ ํ—ค๋”
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

์ด์ œ ์„œ๋ฒ„๋กœ ์ „์†ก๋˜๋Š” ๋ชจ๋“  ์š”์ฒญ๊ณผ ํ•จ๊ป˜, ๋ธŒ๋ผ์šฐ์ €๋Š” Cookie header ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„๋กœ ์ด์ „์— ์ €์žฅํ–ˆ๋˜ ๋ชจ๋“  ์ฟ ํ‚ค๋“ค์„ ํšŒ์‹ ํ•œ๋‹ค.

# ๋ธŒ๋ผ์šฐ์ €
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

์ฟ ํ‚ค์˜ ๋ผ์ดํ”„ํƒ€์ž„ ๐Ÿ˜‡

์ฟ ํ‚ค์˜ ๋ผ์ดํ”„ํƒ€์ž„์€ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Expires(์œ ํšจ ์ผ์ž)๋‚˜ Max-Age(๋งŒ๋ฃŒ ๊ธฐ๊ฐ„) ์˜ต์…˜์ด ์ง€์ •๋˜์–ด์žˆ์ง€ ์•Š์•„์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋‹ซํž ๋•Œ ํ•จ๊ป˜ ์‚ญ์ œ๋œ๋‹ค.
  • ์žฌ์‹œ์ž‘ํ•  ๋•Œ ์„ธ์…˜์„ ๋ณต์›ํ•ด ์„ธ์…˜ ์ฟ ํ‚ค๊ฐ€ ๋ฌด๊ธฐํ•œ ์กด์žฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

์˜์†์ ์ธ ์ฟ ํ‚ค

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
  • Expires ์†์„ฑ์— ๋ช…์‹œ๋œ ๋‚ ์งœ์— ์‚ญ์ œ๋˜๊ฑฐ๋‚˜,Max-Age ์†์„ฑ์— ๋ช…์‹œ๋œ ๊ธฐ๊ฐ„ ์ดํ›„์— ์‚ญ์ œ๋œ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋‹ซ์•„๋„ ์ฟ ํ‚ค๊ฐ€ ์‚ญ์ œ๋˜์ง€ ์•Š๋Š”๋‹ค.


์ด์ œ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์— ๋ณด์•ˆ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.


๋ณด์•ˆ์ด ์ค‘์š”ํ•œ ์ด์œ 

  1. XSS
    • ํด๋ผ์ด์–ธํŠธ ๋ธŒ๋ผ์šฐ์ €์— JavaScript ๋ฅผ ์‚ฝ์ž…ํ•ด ์‹คํ–‰ํ•˜๋Š” ๊ณต๊ฒฉ
      • ๊ณต๊ฒฉ์ž์˜ ์ฝ”๋“œ๊ฐ€ ๋‚ด ์‚ฌ์ดํŠธ์˜ ๋กœ์ง์ธ ์ฒ™ ํ–‰๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. CSRF
    • ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์—์„œ ์šฐ๋ฆฌ ์‚ฌ์ดํŠธ์˜ api ์ฝœ์„ ์š”์ฒญํ•ด ์‹คํ–‰ํ•˜๋Š” ๊ณต๊ฒฉ
      • ๋กœ๊ทธ์ธํ•œ ์ฒ™ ๊ณ„์ขŒ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๊พธ๊ฑฐ๊ฐ€ ์†ก๊ธˆ์„ ๋ณด๋‚ธ๋‹ค.

์œ„ ์ฒ˜๋Ÿผ ์ฟ ํ‚ค๋Š” Client์—์„œ JavaScript ๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ ์„ค์ •์„ ํ•˜์ง€ ์•Š์€ ์ฑ„๋กœ ํ†ต์‹ ์„ ํ•ด๋ฒ„๋ฆฌ๋ฉด ์ฟ ํ‚ค๋ฅผ ๊ฐ€๋กœ์ฑŒ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.
๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์—์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด์„œ ์ด๋Ÿฐ ๊ณต๊ฒฉ๋“ค์€ ๋ฐฉ์–ดํ•ด์•ผ ํ•œ๋‹ค.
๊ทธ ๋ฐฉ๋ฒ•๋“ค์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž.


HttpOnly

HttpOnly ์ฟ ํ‚ค ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด JavaScript๋ฅผ ํ†ตํ•ด ์ฟ ํ‚ค์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๊ฒŒ ๋˜์–ด, ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ด ์ฟ ํ‚ค ๊ฐ’์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์•„์ค€๋‹ค.
HTTP Only Cookie ๋ฅผ ํ™œ์„ฑํ™” ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ฟ ํ‚ค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— HttpOnly ๋ผ๋Š” ์ ‘๋ฏธ์‚ฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

HTTP Only Cookie๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Client์—์„œ Javascript๋ฅผ ํ†ตํ•œ ์ฟ ํ‚ค ํƒˆ์ทจ๋ฌธ์ œ๋ฅผ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ Javascript๊ฐ€ ์•„๋‹Œ ๋„คํŠธ์›Œํฌ๋ฅผ ์ง์ ‘ ๊ฐ์ฒญํ•˜์—ฌ ์ฟ ํ‚ค๋ฅผ ๊ฐ€๋กœ์ฑŒ ์ˆ˜๋„ ์žˆ๋‹ค.
๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ์ •๋ณด ์œ ์ถœ๋“ค์„ ๋ง‰๊ธฐ ์œ„ํ•ด, HTTPS ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ์„œ๋ฒ„์— ๋„˜๊ฒจ์ฃผ๊ฒŒ ๋˜๋ฉด, ํ•ด์ปค๋“ค์ด ์ฟ ํ‚ค๋ฅผ ํƒˆ์ทจํ•ด๋„ ์•”ํ˜ธํ™”๊ฐ€ ๋˜์–ด์žˆ์–ด ์ •๋ณด๋ฅผ ์•Œ์•„๋‚ผ ์ˆ˜ ์—†๋‹ค.

์ด๋ฅผ ์œ„ํ•ด Secure ์ ‘๋ฏธ์‚ฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ฟ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” HTTPS ๊ฐ€ ์•„๋‹Œ ํ†ต์‹ ์—์„œ๋Š” ์ฟ ํ‚ค๋ฅผ ์ „์†กํ•˜์ง€ ์•Š๋Š”๋‹ค.


HttpOnly ์†์„ฑ๊ณผ Secure ์†์„ฑ์„ ์‚ฌ์šฉํ•ด ์ƒ์„ฑํ•œ ์ฟ ํ‚ค์˜ ์˜ˆ์‹œ์ด๋‹ค.

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly


๋กœ๊ทธ์ธ ๐Ÿ”

์ธ์ฆ ๋ฐฉ์‹

๋กœ๊ทธ์ธ ์ธ์ฆ ๋ฐฉ์‹์—๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

  1. ์„ธ์…˜ id ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹
    • ํด๋ผ์ด์–ด์–ธํŠธ ๋กœ๊ทธ์ธ โ‡’ ์„œ๋ฒ„ ์„ธ์…˜ ์ƒ์„ฑ, ์„ธ์…˜์˜ id ์ „์†ก โ‡’ ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅ โ‡’ ์ธ์ฆ์ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์„œ๋ฒ„์— id ์ „์†ก โ‡’ ์„œ๋ฒ„๋Š” ์„ธ์…˜์ด ์œ ํšจํ•œ์ง€ ํ™•์ธ
  2. JWT(ํ† ํฐ) ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹
    • accessToken, refreshToken ์„ ์‚ฌ์šฉ
    • ํด๋ผ์ด์–ธํŠธ ๋กœ๊ทธ์ธ โ‡’ ์„œ๋ฒ„๊ฐ€ ์ธ์ฆ์ •๋ณด(JWT ์•ˆ์— ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‹ด์•„์„œ) ๋ฅผ ์ „์†ก โ‡’ ์ธ์ฆ ์ •๋ณด ์ค‘ accessToken ๊ณผ refreshToken ์„ ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅ โ‡’ accessToken ์„ ๋กœ๊ทธ์ธํ•œ ์œ ์ €์—๊ฒŒ๋งŒ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ์ •๋ณด์— ์ ‘๊ทผํ•  ๋•Œ ์„œ๋ฒ„์— ์ „์†ก โ‡’ ํ† ํฐ์ด ์œ ํšจํ•œ์ง€ ํ™•์ธ
      • ์‹ค์งˆ์ ์ธ ์ธ์ฆ ์ •๋ณด๋Š” accessToken ์ธ๋ฐ ์ด๋Š” ์ผ์ •์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋งŒ๋ฃŒ๋œ๋‹ค.
        • ๋กœ์ปฌ ๋ณ€์ˆ˜์— ์ €์žฅ โ‡’ api ์š”์ฒญํ•  ๋•Œ authorization header์— ๋„ฃ์–ด์„œ ๋ณด๋‚ด์ค€๋‹ค.
      • ์ด๋•Œ refreshToken ์„ ์ด์šฉํ•ด ๋กœ๊ทธ์ธ์„ ์ง€์†์ ์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
        • refreshToken ์„ ์„œ๋ฒ„์— ์ „์†กํ•˜๋ฉด โ‡’ ์„œ๋ฒ„๋Š” ์ƒˆ๋กœ์šด accessToken ์„ ๋ฐœ๊ธ‰ํ•ด์ค€๋‹ค.


์šฐ๋ฆฌ๋Š” ์ด์ค‘์—์„œ ํ† ํฐ(JWT) ๋ฅผ ์ด์šฉํ•œ ๋ฐฉ์‹์œผ๋กœ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ํ•ด์„œ JWT ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค๋‹ค.


JWT (JSON Web Token) ๐Ÿ“ฆ

JSON Web Token ์ด๋ž€?

JSON Web Token ์ด๋ž€ JSON ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ์ปดํŒฉํŠธํ•˜๊ณ  ์ž์ฒด์ ์ธ ๋ฐฉ๋ฒ•์„ JSON ๊ฐ์ฒด๋กœ ์ •์˜ํ•˜๋Š” ๊ฐœ๋ฐฉํ˜• ํ‘œ์ค€์ด๋‹ค.

์•”ํ˜ธํ™”๋‚˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์ถ”๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐํŒจํ‚ค์ง€๋ผ๊ณ ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.


์ด๋Ÿฌํ•œ JWT ๋ฅผ ์–ธ์ œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ?

  1. Authorization
    • ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜๋ฉด, ์ดํ›„ API ์š”์ฒญ๋“ค์€ JWT๋ฅผ ํฌํ•จํ•˜๊ณ , ์‚ฌ์šฉ์ž๋Š” ํ•ด๋‹น ํ† ํฐ์œผ๋กœ ํ—ˆ์šฉ๋œ ๊ฒฝ๋กœ, ์„œ๋น„์Šค ๋ฐ ๋ฆฌ์†Œ์Šค์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. ์ •๋ณด ๊ตํ™˜
    • JWT๋Š” ๊ณต๊ฐœ ํ‚ค์™€ ๊ฐœ์ธ ํ‚ค ์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ช…ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด๋‚ธ ์‚ฌ๋žŒ์ด ์ž์‹ ์ด ๋ˆ„๊ตฌ์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ํ—ค๋”์™€ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ช…์„ ๊ณ„์‚ฐํ•˜๋ฏ€๋กœ ๋‚ด์šฉ์ด ์กฐ์ž‘๋˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

JSON Web Token ๊ตฌ์กฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

JSON Web Token ์€ ์ (.)์œผ๋กœ ๊ตฌ๋ถ„๋œ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

- Header
- Payload
- Signature

์ด๊ฒƒ๋“ค์„ ํ•˜๋‚˜๋กœ ํ•ฉ์น˜๋ฉด ํ•˜๋‚˜์˜ JWT ๊ฐ€ ๋˜๋Š”๋ฐ, ์ด๋Š” ์ ์œผ๋กœ ๊ตฌ๋ถ„๋œ 3๊ฐœ์˜ Base64-URL ๋ฌธ์ž์—ด๋กœ, ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค์Œ์ฒ˜๋Ÿผ ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค.

	xxxxx.yyyyy.zzzzz

์ด๋ ‡๊ฒŒ ์ƒ์„ฑ๋œ JWT๋Š”
jwt.io Debugger ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์•„๋ž˜์ฒ˜๋Ÿผ decode, verify, ๋ฐ ์ƒ์„ฑ ํ•  ์ˆ˜ ์žˆ๋‹ค.



์ด์ œ ์ด JWT ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฆฌ์•กํŠธ์—์„œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด์ž.



๋ฆฌ์•กํŠธ์—์„œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ

https://velog.io/@yaytomato/ํ”„๋ก ํŠธ์—์„œ-์•ˆ์ „ํ•˜๊ฒŒ-๋กœ๊ทธ์ธ-์ฒ˜๋ฆฌํ•˜๊ธฐ

์œ„ ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.
๋ฐฑ์—”๋“œ์™€ ๋งž์ถฐ๋ณธ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ์ŠคํŽ™์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. refreshToken ์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  2. Cookie ๋ฅผ ์ด์šฉํ•ด์„œ accessToken ์„ ๊ด€๋ฆฌํ•œ๋‹ค.
    • local์ผ ๊ฒฝ์šฐ์—๋งŒ ๊ฐœ๋ฐœ ํŽธ์˜์„ฑ์„ ์œ„ํ•ด์„œ localStorage ๋ฅผ ์ด์šฉํ•˜์—ฌ token ์„ ๊ด€๋ฆฌํ•œ๋‹ค.
  3. Secure Cookie ์™€ HTTP Only Cookie ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ณด์•ˆ์„ ๋†’์ธ๋‹ค.
  4. Secure Cookie ์ „๋‹ฌ์„ ์œ„ํ•ด์„œ ํ”„๋ก ํŠธ์™€ ๋กœ๊ทธ์ธ API๋ฅผ ์ œ๊ณตํ•  ๋ฐฑ์—”๋“œ๋Š” ๊ฐ™์€ ๋„๋ฉ”์ธ์„ ๊ณต์œ ํ•œ๋‹ค.
  5. ๋ฐฑ์—”๋“œ๋Š” HTTP ์‘๋‹ต Set-Cookie ํ—ค๋”์— accessToken ๊ฐ’์„ ๋‹ด์•„์„œ ๋ณด๋‚ด์ค€๋‹ค.


์ด์ œ ํ”„๋ก ํŠธ์—์„œ ํ•ด์ค˜์•ผ ํ•˜๋Š” ์ผ๋“ค์„ ์‚ดํŽด๋ณด์ž.


  1. ๋‹ค๋ฅธ ๋„๋ฉ”์ธ๋“ค๋ผ๋ฆฌ ์ž๊ฒฉ ์ธ์ฆ ์ •๋ณด๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์˜ต์…˜์„ ์„ค์ •ํ•œ๋‹ค. (withCredentials = true)

    ๋งŒ์•ฝ ํด๋ผ์ด์–ธํŠธ ์„œ๋ฒ„์™€ API ์„œ๋ฒ„๊ฐ€ Host ๋Š” ๊ฐ™์ง€๋งŒ, Port ๊ฐ€ ๋‹ค๋ฅด๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

     - ํด๋ผ์ด์–ธํŠธ ์„œ๋ฒ„ : http://localhost:3000
      - API ์„œ๋ฒ„ : http://localhost:8080

    ์•„๋ฌด ์„ค์ •๋„ ํ•˜์ง€ ์•Š์€ ์ฑ„ axios ๋กœ ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ์„œ๋ฒ„์— ๋ณด๋‚ธ๋‹ค๋ฉด CORS(Cross-origin-resource-sharing ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ ) ์—๋Ÿฌ์™€ ํ•จ๊ป˜ ๋ธŒ๋ผ์šฐ์ €์˜ Cookie ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋‹ด๊ฒจ์žˆ์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.

    ์™œ ์ด๋Ÿฐ์ผ์ด ์ผ์–ด๋‚˜๋Š” ๊ฑธ๊นŒ?

    ์—ฌ๊ธฐ์„œ ์šฐ์„  credential ์ด๋ž€, ์ฟ ํ‚ค๋‚˜ย Authorization ์ธ์ฆ ํ—ค๋” ๋“ฑ์„ ๋‚ดํฌํ•˜๋Š” ์ž๊ฒฉ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋งํ•œ๋‹ค.

    ๊ทธ๋Ÿฐ๋ฐ ๋ธŒ๋ผ์šฐ์ €๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฟ ํ‚ค์™€ ๊ฐ™์€ ์ธ์ฆ๊ณผ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๋ถ€๋กœ request/response header ์— ๋‹ด์ง€ ์•Š๋„๋ก ๋˜์–ด์žˆ๋‹ค.
    ๊ทธ๋ž˜์„œ ๋งŒ์•ฝ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ(Cross Origin)๋ผ๋ฆฌ ์ฟ ํ‚ค์™€ ๊ฐ™์€ ์ž๊ฒฉ ์ธ์ฆ์ •๋ณด๋ฅผ ๊ณต์œ (resource sharing) ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์—์„œ ์ˆ˜๋™์œผ๋กœ ์ด๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ์˜ต์…˜์„ ๊ฐ๊ฐ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

    ํด๋ผ์ด์–ธํŠธ์—์„œ๋„ withCredentials ์˜ต์…˜์„ ์„ค์ •ํ•ด์ค˜์•ผ ํ•˜๊ณ ,
    ์„œ๋ฒ„์—์„œ๋„ Access-Control-Allow-Credentials ํ—ค๋”๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

    ์šฐ๋ฆฌ๋Š” axios ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, aixos ๊ณต์‹ docs ๋ฅผ ํ†ตํ•ด withCredentials ์˜ต์…˜์„ ์„ค์ •ํ•ด๋ณด์ž.
    withCredentials ์˜ ์ฃผ์„์„ ๋ณด๋ฉด ์ž๊ฒฉ ์ฆ๋ช…(credential)์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์ดํŠธ ๊ฐ„ ์ ‘๊ทผ ์ œ์–ด๋ฅผ ํ•ด์•ผ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
    ์ด์ œ ์ „์—ญ Axios Config ์— ์ด withCredentials ์˜ต์…˜์„ true ๋กœ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

    axios.defaults.baseURL = "https://www.abc.com";
    axios.defaults.withCredentials = true;

  1. ๋กœ๊ทธ์ธ API ๋ฅผ ์š”์ฒญํ•œ๋‹ค.

     onLogin = (email, password) => {
       const data = {
         email,
         password,
       };
       axios.post('/login', data).then(response => {
         const { accessToken } = response.data;
    
         // API ์š”์ฒญํ•˜๋Š” ์ฝœ๋งˆ๋‹ค ํ—ค๋”์— accessToken ๋‹ด์•„ ๋ณด๋‚ด๋„๋ก ์„ค์ •
         axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
         // local ์ด ์•„๋‹Œ ํ™˜๊ฒฝ์—์„œ๋Š” ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ๊ฐ€ ๊ฐ™์€ ๋„๋ฉ”์ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด 
         // token ์ด Cookie ์— ๋‹ด๊ฒจ ๋ณด๋‚ด์ง€๊ธฐ ๋•Œ๋ฌธ์— 
         // token ์ด ๋งŒ๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ถ”๊ฐ€์ ์ธ ์„ค์ •์—†์ด ๋‹ค๋ฅธ API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
         
         // local ์ผ ๊ฒฝ์šฐ์—๋งŒ storage ์— token ์„ ์ €์žฅํ•ด์„œ 
         // API ํ†ต์‹ ๋งˆ๋‹ค ํ—ค๋”์— token ์„ ๋‹ด์•„ ๋ณด๋‚ด๋„๋ก ์„ค์ •ํ•œ๋‹ค.
       }).catch(error => {
         // ... ์—๋Ÿฌ ์ฒ˜๋ฆฌ
         // ex) 401 unauthorized ์ผ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•œ๋‹ค.
       });
     }

    ์œ„์— ์ ์–ด๋†“์€ ๋ฐ”์™€ ๊ฐ™์ด
    local ์ด ์•„๋‹Œ ํ™˜๊ฒฝ์—์„œ๋Š” ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ๊ฐ€ ๊ฐ™์€ ๋„๋ฉ”์ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด
    token ์ด Cookie ์— ๋‹ด๊ฒจ ๋ณด๋‚ด์ง€๊ธฐ ๋•Œ๋ฌธ์— token ์ด ๋งŒ๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ถ”๊ฐ€์ ์ธ ์„ค์ •์—†์ด ๋‹ค๋ฅธ API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    ๋งŒ์•ฝ token ์ด ๋งŒ๋ฃŒ๋œ ํ›„์˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด catch ์ ˆ์—์„œ 401 unauthorized ์ผ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

    ๋˜ํ•œ local ์ผ ๊ฒฝ์šฐ์—๋งŒ ๊ฐœ๋ฐœ ํŽธ์˜์„ฑ์„ ์œ„ํ•ด storage ์— token ์„ ์ €์žฅํ•ด์„œ API ํ†ต์‹ ๋งˆ๋‹ค ํ—ค๋”์— token ์„ ๋‹ด์•„ ๋ณด๋‚ด๋„๋ก ์„ค์ •ํ•˜์˜€๋‹ค.
    ์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋ฉด ์ƒˆ๋กœ๊ณ ์นจ์ด ๋˜์–ด๋„ token ์ด ๋‚ ์•„๊ฐ€๋Š” ์ผ์€ ์—†์„ ๊ฒƒ์ด๋‹ค...!


  1. ๋กœ๊ทธ์ธ ๋งŒ๋ฃŒ ๋ฐ ๋กœ๊ทธ์ธ ์—ฐ์žฅ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค€๋‹ค.

    ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ๋‹ด๊ณ ์žˆ๋Š” token ์ด ๋งŒ๋ฃŒ๋˜์—ˆ์„ ๊ฒฝ์šฐ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค.
    ์šฐ๋ฆฌ๋Š” API ํ†ต์‹  ์‹œ 401 unauthorized ์ผ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•˜์˜€๋‹ค.

    ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ• ๋Œ€์‹ 
    ์œ ์ €๊ฐ€ ๋ชจ๋ฅด๊ฒŒ ์„œ๋ฒ„์—์„œ ์ƒˆ๋กœ์šด token ์„ ๋ฐ›์•„์™€์„œ ์กฐ์šฉํžˆ ์ž๋™์œผ๋กœ ๋กœ๊ทธ์ธ์ด ์—ฐ์žฅ๋˜๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งŽ์€ ์›น์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉํ•˜๊ณค ํ•˜๋Š”๋ฐ,
    ์ด๋ฅผ Silent Refresh ๋ผ๊ณ  ํ•œ๋‹ค.



์ฐธ๊ณ 

profile
์˜๊ฒฌ์ด ์žˆ์œผ์‹œ๊ฑฐ๋‚˜ ์ž˜๋ชป๋œ ๋‚ด์šฉ์„ ๋ฐœ๊ฒฌํ•˜์…จ๋‹ค๋ฉด ์žฌ๋นจ๋ฆฌ ๋Œ“๊ธ€ ์ฃผ์„ธ์š” ๏ฝกโ€ขโ—กโ€ข๏ฝก

4๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2024๋…„ 1์›” 22์ผ

์•ˆ๋…•ํ•˜์„ธ์š”! ์ข‹์€ ๊ธ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค:)
๊ถ๊ธˆํ•œ๊ฒŒ ์žˆ๋Š”๋ฐ์š”..
axios.defaults.headers.common['Authorization'] = Bearer ${accessToken};
์— ํ† ํฐ ์ €์žฅํ•ด๋‘๋ฉด ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ์•ˆ๋‚ ๋ผ๊ฐ€๋‚˜์š”?

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2024๋…„ 9์›” 3์ผ

์ข‹์€ ๊ธ€ ์ •๋ง ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

1๊ฐœ์˜ ๋‹ต๊ธ€