๐Ÿช ํ”„๋ก ํŠธ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌํ•˜๊ธฐ (ft. React)

yaytomatoยท2020๋…„ 7์›” 5์ผ
156

React

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

๐Ÿงžโ€โ™‚๏ธTL;DR

  • JWT๋ฅผ ์“ด๋‹ค.
  • refreshToken์€ httpOnly ์ฟ ํ‚ค๋กœ, accessToken์€ JSON payload๋กœ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค.
  • ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด mount ๋  ๋•Œ ๋งˆ๋‹ค refreshToken์„ ์ด์šฉํ•ด ์ƒˆ๋กœ์šด accessToken์„ ๋ฐ›์•„์™€ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ์ง€์—ญ ๋ณ€์ˆ˜์— ์ €์žฅํ•˜๊ณ  ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ด ๋ฐฉ์‹์œผ๋กœ CSRF ์ทจ์•ฝ์  ๊ณต๊ฒฉ๊ณผ (๋‹ค๋ฅธ ์„ ํƒ์ง€๋ณด๋‹ค) XSS ๊ณต๊ฒฉ์—์„œ ์•ˆ์ „ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด XSS ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•˜๋‹ค๋ฉด ์–ด๋–ค ๋ฐฉ์‹์„ ์„ ํƒํ•˜๋˜ ๋ณด์•ˆ์ด ์œ„ํ—˜ํ•˜๊ธฐ์— ๊ผญ XSS ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.

ํด๋ผ์ด์–ธํŠธ์—์„œ ์•ˆ์ „ํ•œ ๋กœ๊ทธ์ธ / ์œ ์ € ์ธ์ฆ (Authentication) ๋ฐฉ์‹์— ๋Œ€ํ•ด ์–˜๊ธฐํ•˜๋ ค๋ฉด ๋‹ค์Œ ์„ธ ๊ฐ€์ง€๋ฅผ ๋จผ์ € ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

  1. ๋กœ๊ทธ์ธ์€ ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์ง€๋‚˜
  2. ๋ณด์•ˆ์€ ์–ด๋–ป๊ฒŒ ๋šซ๋ฆฌ๋‚˜
  3. ๋ธŒ๋ผ์šฐ์ € ์ €์žฅ์†Œ ์ข…๋ฅ˜์™€ ๋ณด์•ˆ ์ด์Šˆ (localStorage, ์ฟ ํ‚ค, httpOnly ์ฟ ํ‚ค)

๋จผ์ € ์œ ์ € ์ธ์ฆ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ถ€ํ„ฐ ์ด์•ผ๊ธฐํ•ด๋ณด์ž. ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์ด ์žˆ๋‹ค.

๐Ÿ”‘ ๋กœ๊ทธ์ธ์€ ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์ง€๋‚˜

  1. ์„ธ์…˜ id๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹

    ์ด ๋ฐฉ์‹์—์„œ ์„œ๋ฒ„๋Š” ํŠน์ • ์œ ์ €์˜ ์ •๋ณด๋ฅผ ๋‹ด์€ ์„ธ์…˜์„ ์ƒ์„ฑํ•œ๋‹ค. (1) ์œ ์ €๊ฐ€ ๋กœ๊ทธ์ธํ•  ๋•Œ (2) ์„œ๋ฒ„๋Š” ์„ธ์…˜์„ ์ƒ์„ฑํ•œ ํ›„ (3) ๊ทธ ์„ธ์…˜์˜ id๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ณด๋‚ด์ฃผ๊ณ  (4) ํด๋ผ์ด์–ธํŠธ๋Š” ์ด id๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅํ•ด๋‘์—ˆ๋‹ค๊ฐ€ (5) ์ธ์ฆ์ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์„œ๋ฒ„์— id ๊ฐ’์„ ๋ณด๋‚ด๋ฉด (6) ์„œ๋ฒ„๋Š” ๊ทธ id๋ฅผ ํ†ตํ•ด ์„ธ์…˜์„ ๋ถˆ๋Ÿฌ์™€ ์œ ํšจํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ธ์ฆํ•œ๋‹ค.

  2. JWT๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹ (ft. refreshToken, accessToken)

    ๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ (1) ์œ ์ €๊ฐ€ ๋กœ๊ทธ์ธํ•  ๋•Œ (2) ์„œ๋ฒ„๊ฐ€ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋ณด๋‚ด์ฃผ๋Š”๋ฐ, ์•”ํ˜ธํ™”๋‚˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์ถ”๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ํŒจํ‚ค์ง€์•ˆ์— ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‹ด์•„ ๋ณด๋‚ด์ค€๋‹ค. (์ด ํŒจํ‚ค์ง€๊ฐ€ JSON Web Token ์ฆ‰ JWT๋‹ค.) (3) ๋‹ด๊ธฐ๋Š” ์ •๋ณด ์ค‘ accessToken๊ณผ refreshToken์ด ์ดํ›„ ์œ ์ € ์ธ์ฆ์— ์‚ฌ์šฉ๋˜๋Š”๋ฐ (4) ์ด ์ •๋ณด๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅํ•ด๋‘”๋‹ค.

    ์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๋ฉด ์‹ค์งˆ์ ์ธ ์ธ์ฆ ์ •๋ณด๋Š” accessToken์ธ๋ฐ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋งŒ๋ฃŒํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ๋‹ค. (๋กœ๊ทธ์ธ ํ›„ ๋ฉฐ์น  ๋’ค ๋กœ๊ทธ์ธ์ด ๋งŒ๋ฃŒ๋ผ์„œ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์•ผ ํ–ˆ๋˜ ๊ฒฝํ—˜์ด ์žˆ๋Š”๊ฐ€?) refreshToken์„ ์ด์šฉํ•ด ๋กœ๊ทธ์ธ์„ ์ง€์†์ ์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. refreshToken์„ ์„œ๋ฒ„์— ๋ณด๋‚ด๋ฉด ๊ทธ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด accessToken์„ ๋ฐœ๊ธ‰ํ•ด ๋Œ๋ ค์ฃผ๋Š” ๊ฑฐ๋‹ค. refreshToken ์‚ฌ์šฉ์€ ์˜ต์…˜์ด๋‹ค.

    ๊ทธ๋Ÿผ ๋‹ค์‹œ ๋Œ์•„์™€ (5) ์ด accessToken์„ ์œ ์ €์—๊ฒŒ๋งŒ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ์ •๋ณด์— ์ ‘๊ทผํ•  ๋•Œ ์„œ๋ฒ„์— ๋ณด๋‚ด๋ฉด (6) ์„œ๋ฒ„๋Š” ๊ทธ ํ† ํฐ์ด ์œ ํšจํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ธ์ฆํ•œ๋‹ค.

๋‹ค์Œ์œผ๋กœ ๋ณด์•ˆ ๊ณต๊ฒฉ์˜ ์ข…๋ฅ˜์™€ ํŠน์ง•์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž. ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ ์ทจ์•ฝ์  ์ค‘ ์œ ์ € ์ธ์ฆ์—์„œ ๋ณดํŽธ์ ์œผ๋กœ ์ด์šฉ๋˜๋Š” ์ทจ์•ฝ์ ์€ ๋‘ ๊ฐ€์ง€๋‹ค: XSS์™€ CSRF

๐Ÿ˜ˆ ๋ณด์•ˆ์€ ์–ด๋–ป๊ฒŒ ๋šซ๋ฆฌ๋‚˜

  1. XSS ๊ณต๊ฒฉ

    ๊ณต๊ฒฉ์ž(ํ•ด์ปค)๊ฐ€ ํด๋ผ์ด์–ธํŠธ ๋ธŒ๋ผ์šฐ์ €์— Javascript๋ฅผ ์‚ฝ์ž…ํ•ด ์‹คํ–‰ํ•˜๋Š” ๊ณต๊ฒฉ์ด๋‹ค. ๊ณต๊ฒฉ์ž๊ฐ€ <input>์„ ํ†ตํ•ด Javascript๋ฅผ ์„œ๋ฒ„๋กœ ์ „์†กํ•ด ์„œ๋ฒ„์—์„œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, url์— Javascript๋ฅผ ์ ์–ด ํด๋ผ์ด์–ธํŠธ์—์„œ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๊ณต๊ฒฉ์ž๊ฐ€ ์‚ฌ์ดํŠธ์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฝ์ž…ํ•ด XSS ๊ณต๊ฒฉ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ๊ณต๊ฒฉ์ž๋Š” Javascript๋ฅผ ํ†ตํ•ด ์‚ฌ์ดํŠธ์˜ ๊ธ€๋กœ๋ฒŒ ๋ณ€์ˆซ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ๊ทธ ๊ฐ’์„ ์ด์šฉํ•ด ์‚ฌ์ดํŠธ์ธ ์ฒ™ API ์ฝœ์„ ์š”์ฒญํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜๋ฉด ๊ณต๊ฒฉ์ž์˜ ์ฝ”๋“œ๊ฐ€ ๋‚ด ์‚ฌ์ดํŠธ์˜ ๋กœ์ง์ธ ์ฒ™ ํ–‰๋™ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑฐ๋‹ค.

  2. CSRF ๊ณต๊ฒฉ

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

React์—์„œ(= ํด๋ผ์ด์–ธํŠธ) ์œ„ ํ”„๋กœ์„ธ์Šค๋“ค์„ ๋”ฐ๋ผ ์„ธ์…˜ id๋‚˜ accessToken ๊ฐ™์€ ์ธ์ฆ์ •๋ณด๋ฅผ ์ €์žฅํ•  ๋•Œ (์ธ์ฆ ๋ฐฉ์‹ 4๋ฒˆ ๋‹จ๊ณ„ ํ•ด๋‹น) ์ด์šฉํ•˜๋Š” ์ €์žฅ์†Œ๋Š” ๋ณดํ†ต localStorage๋‚˜ ์ฟ ํ‚ค๋‹ค. ํŽ˜์ด์ง€๋ฅผ ๋ฆฌํ”„๋ ˆ์‹œ ํ•˜๊ฑฐ๋‚˜ ์ฐฝ์„ ๋‹ซ๊ณ  ๋‹ค์‹œ ์ ‘์†ํ•  ๋•Œ๋„ ๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์ด์–ด์ง€๋„๋ก ๋‘˜ ๋‹ค ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ํ•˜์ง€๋งŒ ๋‘ ๋ฐฉ์‹์€ XSS์™€ CSRF ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ˜ฑ ๋ธŒ๋ผ์šฐ์ € ์ €์žฅ์†Œ ์ข…๋ฅ˜์™€ ๋ณด์•ˆ ์ด์Šˆ

  1. localStorage ์ €์žฅ ๋ฐฉ์‹

    ๋ธŒ๋ผ์šฐ์ € ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. Javascript ๋‚ด ๊ธ€๋กœ๋ฒŒ ๋ณ€์ˆ˜๋กœ ์ฝ๊ธฐ / ์“ฐ๊ธฐ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

    ๐Ÿ˜ˆ : localStorage ์•ˆ์— ์„ธ์…˜ id, refreshToken ๋˜๋Š” accessToken์„ ์ €์žฅํ•ด๋‘๋ฉด XSS ์ทจ์•ฝ์ ์„ ํ†ตํ•ด ๊ทธ ์•ˆ์— ๋‹ด๊ธด ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ค๊ฑฐ๋‚˜, ๋ถˆ๋Ÿฌ์˜จ ๊ฐ’์„ ์ด์šฉํ•ด API ์ฝœ์„ ์œ„์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.

  2. ์ฟ ํ‚ค ์ €์žฅ ๋ฐฉ์‹

    ๋ธŒ๋ผ์šฐ์ €์— ์ฟ ํ‚ค๋กœ ์ €์žฅ๋˜๋Š”๋ฐ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ HTTP ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ฟ ํ‚ค๊ฐ€ ์„œ๋ฒ„์— ์ „์†ก๋œ๋‹ค. Javascript ๋‚ด ๊ธ€๋กœ๋ฒŒ ๋ณ€์ˆ˜๋กœ ์ฝ๊ธฐ / ์“ฐ๊ธฐ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

    ๐Ÿ˜ˆ : ์ฟ ํ‚ค ์ €์žฅ ๋ฐฉ์‹ ์—ญ์‹œ ์•ˆ์— ์„ธ์…˜ id, refreshToken, accessToken์„ ์ €์žฅํ•ด๋‘๋ฉด XSS ์ทจ์•ฝ์ ์ด ์žˆ์„ ๋•Œ ๋‹ด๊ธด ๊ฐ’๋“ค์„ ๋ถˆ๋Ÿฌ์˜ค๊ฑฐ๋‚˜, API ์ฝœ์„ ๋ณด๋‚ด๋ฉด ์ฟ ํ‚ค์— ๋‹ด๊ธด ๊ฐ’๋“ค์ด ํ•จ๊ป˜ ์ „์†ก๋˜์–ด ๋กœ๊ทธ์ธํ•œ ์ฒ™ ๊ณต๊ฒฉ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๐Ÿ˜ˆ : ์ฟ ํ‚ค์— ์„ธ์…˜ id๋‚˜ accessToken์„ ์ €์žฅํ•ด ์ธ์ฆ์— ์ด์šฉํ•˜๋Š” ๊ตฌ์กฐ์— CSRF ์ทจ์•ฝ์ ์ด ์žˆ๋‹ค๋ฉด ์ธ์ฆ ์ •๋ณด๊ฐ€ ์ฟ ํ‚ค์— ๋‹ด๊ฒจ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด์ง„๋‹ค. ๊ณต๊ฒฉ์ž๋Š” ์œ ์ € ๊ถŒํ•œ์œผ๋กœ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์•ก์…˜์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๐Ÿ˜‡ : ์ฟ ํ‚ค์— refreshToken๋งŒ ์ €์žฅํ•˜๊ณ  ์ƒˆ๋กœ์šด accessToken์„ ๋ฐ›์•„์™€ ์ธ์ฆ์— ์ด์šฉํ•˜๋Š” ๊ตฌ์กฐ์—์„œ๋Š” CSRF ์ทจ์•ฝ์  ๊ณต๊ฒฉ์„ ๋ฐฉ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค. refreshToken์œผ๋กœ accessToken์„ ๋ฐ›์•„๋„ accessToken์„ ์Šคํฌ๋ฆฝํŠธ์— ์‚ฝ์ž…ํ•  ์ˆ˜ ์—†๋‹ค๋ฉด accessToken์„ ์‚ฌ์šฉํ•ด ์œ ์ € ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

  3. httpOnly ์ฟ ํ‚ค ์ €์žฅ ๋ฐฉ์‹

    ๋ธŒ๋ผ์šฐ์ €์— ์ฟ ํ‚ค๋กœ ์ €์žฅ๋˜๋Š” ๊ฑด ๊ฐ™์ง€๋งŒ, Javascript ๋‚ด์—์„œ ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

    ๐Ÿ˜‡ : httpOnly ์ฟ ํ‚ค ๋ฐฉ์‹์œผ๋กœ ์ €์žฅ๋œ ์ •๋ณด๋Š” XSS ์ทจ์•ฝ์  ๊ณต๊ฒฉ์œผ๋กœ ๋‹ด๊ธด ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†๋‹ค.

    ๐Ÿ˜‡ : httpOnly ์ฟ ํ‚ค ์—ญ์‹œ refreshToken๋งŒ ์ €์žฅํ•˜๊ณ  accessToken์„ ๋ฐ›์•„์™€ ์ธ์ฆ์— ์ด์šฉํ•˜๋Š” ๊ตฌ์กฐ๋กœ CSRF ๊ณต๊ฒฉ ๋ฐฉ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

    ๐Ÿ˜ˆ : ์ฟ ํ‚ค ์ €์žฅ ๋ฐฉ์‹๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ ์„ธ์…˜ id, accessToken์€ ์ €์žฅํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

    ๐Ÿ˜ˆ : httpOnly ์ฟ ํ‚ค์— ๋‹ด๊ธด ๊ฐ’์— ์ ‘๊ทผํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ XSS ์ทจ์•ฝ์ ์„ ๋…ธ๋ ค API ์ฝœ์„ ์š”์ฒญํ•˜๋ฉด httpOnly ์ฟ ํ‚ค์— ๋‹ด๊ธด ๊ฐ’๋“ค๋„ ํ•จ๊ป˜ ๋ณด๋‚ด์ ธ ์œ ์ €์ธ ์ฒ™ ์ •๋ณด๋ฅผ ๋นผ์˜ค๊ฑฐ๋‚˜ ์•ก์…˜์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

์–ด๋–ค ์ €์žฅ ๋ฐฉ์‹์„ ํƒํ•ด๋„ XSS ์ทจ์•ฝ์ ์ด ์žˆ๋‹ค๋ฉด ๋ณด์•ˆ ์ด์Šˆ๊ฐ€ ์กด์žฌํ•œ๋‹ค (XSS๋กœ API ์ฝœ์„ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์œผ๋กœ ๋‹ค ๋šซ๋ฆฐ๋‹ค). ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์œ ์ € ์ •๋ณด ์ €์žฅ ๋ฐฉ์‹์„ ๋ฐ”๊พธ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ๋ฐฉ์–ดํ•  ์ˆ˜ ์—†๊ณ , ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์—์„œ ์ถ”๊ฐ€์ ์œผ๋กœ XSS ๋ฐฉ์–ด ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์ˆ˜๋‹ค.

์˜ˆ: <input>์—์„œ ์ž…๋ ฅ๋œ ๊ฐ’์ด html / Javascript๋กœ ์ธ์‹๋˜์ง€ ์•Š๋„๋ก ์„œ๋ฒ„์—์„œ escape ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค€๋‹ค. ๋˜ url์„ ํ†ตํ•ด Javascript๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋„๋ก ๋ผ์šฐํŒ…์„ ๊ผผ๊ผผํ•˜๊ฒŒ ๊ด€๋ฆฌํ•œ๋‹ค. ๋‹คํ–‰์ธ ๊ฒƒ์€ React๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ string์— html / Javascript๋ฅผ ๋‹ด์•„ JSX์— ์‚ฝ์ž…ํ•  ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ escape ์ฒ˜๋ฆฌํ•œ๋‹ค. (XSS ๋ฐฉ์–ด ์ฒ˜๋ฆฌ๋Š” ๋˜ ๋‹ค๋ฅธ ์ฃผ์ œ๊ธฐ์— ์—ฌ๊ธฐ์„œ๋Š” ์ด ์ •๋„์—์„œ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค.)

๐Ÿค” ๊ทธ๋ž˜์„œ ๋กœ๊ทธ์ธ์„ ์–ด๋–ป๊ฒŒ ํ•œ๋‹ค๋Š” ๊ฑฐ์•ผ?

๋ณธ๋ก ์— ๋“ค์–ด๊ฐ€๊ธฐ ์œ„ํ•œ ์ค€๋น„๋Š” ๋งˆ์ณค๋‹ค.

์ข…ํ•ฉํ•ด๋ณด๋ฉด ์„ธ์…˜ id๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์€ ์–ด๋–ค ๋ฐฉ์‹์ด๋˜์ง€ ๋ณด์•ˆ ์œ„ํ—˜์š”์†Œ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ JWT ์ด์šฉํ•œ ์ธ์ฆ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. refreshToken๋งŒ์„ httpOnly ์ฟ ํ‚ค์— ์ €์žฅํ•ด CSRF ๊ณต๊ฒฉ์„ ๋ฐฉ์–ดํ•  ๊ฒƒ์ด๋‹ค. accessToken์€ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ๋กœ์ปฌ ๋ณ€์ˆ˜์— ์ €์žฅํ•ด ์‚ฌ์šฉํ•˜๋ฉฐ, API๋ฅผ ์š”์ฒญํ•  ๋•Œ Authorization ํ—ค๋”์— ๋„ฃ์–ด ๋ณด๋‚ด์ค€๋‹ค. XSS ์ทจ์•ฝ์ ์„ ์ด์šฉํ•œ API ์š”์ฒญ ๊ณต๊ฒฉ์€ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์—์„œ ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐฉ์–ด ํ•ด์•ผ ํ•œ๋‹ค.

์ •๋ฆฌ

  • JWT๋กœ ์œ ์ € ์ธ์ฆ
  • refreshToken์„ httpOnly ์ฟ ํ‚ค๋กœ, accessToken์€ JSON payload๋กœ ๋ฐ›์•„์™€์„œ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ๋กœ์ปฌ ๋ณ€์ˆ˜๋กœ ์ด์šฉ
  • ์ด๋ฅผ ํ†ตํ•ด CSRF ์ทจ์•ฝ์  ๊ณต๊ฒฉ ๋ฐฉ์–ดํ•˜๊ณ , XSS ์ทจ์•ฝ์  ๊ณต๊ฒฉ์œผ๋กœ ์ €์žฅ๋œ ์œ ์ € ์ •๋ณด ์ฝ๊ธฐ๋Š” ๋ง‰์„ ์ˆ˜ ์žˆ์Œ
  • ํ•˜์ง€๋งŒ XSS ์ทจ์•ฝ์ ์„ ํ†ตํ•ด API ์ฝœ์„ ๋ณด๋‚ผ ๋•Œ๋Š” ๋ฌด๋ฐฉ๋น„ํ•˜๋‹ˆ XSS ์ž์ฒด๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๋ชจ๋‘ ๋…ธ๋ ฅํ•ด์•ผ ํ•จ

๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป React์— ์ ์šฉํ•˜๊ธฐ

์ค€๋น„๋ฌผ

๋กœ๊ทธ์ธ API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฐฑ์—”๋“œ๋Š” HTTP ์‘๋‹ต Set-Cookie ํ—ค๋”์— refreshToken ๊ฐ’์„ ์„ค์ •ํ•˜๊ณ  accessToken ์„ JSON payload์— ๋‹ด์•„ ๋ณด๋‚ด์ค˜์•ผ ํ•œ๋‹ค.

ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜๋ฆฌํ•˜๊ธฐ

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}`;

		// accessToken์„ localStorage, cookie ๋“ฑ์— ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค!

	}).catch(error => {
		// ... ์—๋Ÿฌ ์ฒ˜๋ฆฌ
	});
}

๐ŸŽ ๋ณด๋„ˆ์Šค

๋กœ๊ทธ์ธ ๋งŒ๋ฃŒ, ๋กœ๊ทธ์ธ ์—ฐ์žฅ ์ฒ˜๋ฆฌํ•˜๊ธฐ

์ด ์„น์…˜์€ zeneo๋‹˜ ์งˆ๋ฌธ์„ ๋ฐ›๊ณ  ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

JWT ์ธ์ฆ ๋ฐฉ์‹์—์„œ ์‹ค์งˆ์ ์œผ๋กœ ์ธ์ฆ๋˜์—ˆ๋‚˜๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์€ accessToken์ด๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ํƒํ•œ ๊ตฌ์กฐ์—์„œ ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅ๋œ ๊ฐ’์€ ์ฟ ํ‚ค์— ์ €์žฅ๋œ refreshToken๋ฟ์ด๊ธฐ์— ๋กœ์ปฌ์— ์ €์žฅ๋œ accessToken์€ ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์ด ๊บผ์ง€๊ฑฐ๋‚˜ ํŽ˜์ด์ง€๊ฐ€ ๋ฆฌํ”„๋ ˆ์‹œ ๋˜๋Š” ๋“ฑ ํŽ˜์ด์ง€๊ฐ€ ๋ฆฌ๋กœ๋“œ ๋˜๋ฉด ์‚ฌ๋ผ์ง„๋‹ค. ๋˜ํ•œ, accessToken์€ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋งŒ๋ฃŒ๋œ๋‹ค. ๊ทธ๋ž˜์„œ (1) ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์„œ accessToken์„ ๋ฐ›์•„์˜ค๋Š” ๋ณดํ†ต ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ณด๋‹ค ์™„์„ฑ๋„ ์žˆ๋Š” ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด (2) accessToken์ด ๋งŒ๋ฃŒ๋์„ ๋•Œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ (์˜ˆ๋ฅผ ๋“ค์–ด ์€ํ–‰ ์‚ฌ์ดํŠธ๊ฐ™์ด ๋ณด์•ˆ์ด ์ค‘์š”ํ•œ ์„œ๋น„์Šค๋ผ๋ฉด ์•„์˜ˆ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜๋„๋ก ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๊ณ , ์•„๋‹ˆ๋ฉด ์œ ์ € ๋ชจ๋ฅด๊ฒŒ ์„œ๋ฒ„์—์„œ ์ƒˆ๋กœ์šด accessToken์„ ๋ฐ›์•„์™€์„œ ๋กœ๊ทธ์ธ์ด ์—ฐ์žฅ๋˜๋„๋ก ํ•  ์ˆ˜๋„ ์žˆ์Œ), (3) ํŽ˜์ด์ง€ ๋ฆฌ๋กœ๋“œ ๋  ๋•Œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ (2๋ฒˆ๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด ์žˆ์Œ)๋„ ๊ฒฐ์ •ํ•ด์•ผ ํ•œ๋‹ค.

์ด ์˜ˆ์ œ์—์„œ๋Š” ์œ ์ €๊ฐ€ ๋‹ค์‹œ ์ง์ ‘ ๋กœ๊ทธ์ธํ•˜๋„๋ก ์œ ๋„ํ•˜์ง€ ์•Š๊ณ  ์กฐ์šฉํžˆ ์ž๋™์œผ๋กœ ๋กœ๊ทธ์ธ ์—ฐ์žฅํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ๋‹ค. (์˜์–ด๋กœ silent refresh๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.)

์ฒ˜๋ฆฌํ•  "๋กœ๊ทธ์ธ" ์ผ€์ด์Šค๋“ค

  1. ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ฒดํฌํ•˜๋Š” ๋ณดํ†ต ๋กœ๊ทธ์ธ
  2. accessToken์ด ๋งŒ๋ฃŒ๋์„ ๋•Œ ๋กœ๊ทธ์ธ ์—ฐ์žฅ ์ฒ˜๋ฆฌ
  3. ํŽ˜์ด์ง€ ๋ฆฌ๋กœ๋“œ ๋  ๋•Œ ๋กœ๊ทธ์ธ ์—ฐ์žฅ ์ฒ˜๋ฆฌ

์ค€๋น„๋ฌผ

์œ„์—์„œ ์ด์•ผ๊ธฐํ•œ 3๊ฐ€์ง€ ์ผ€์ด์Šค์— ๋Œ€์‘ํ•˜๊ธฐ ์œ„ํ•ด API๊ฐ€ 2๊ฐœ ํ•„์š”ํ•˜๋‹ค.

  1. POST /login: ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณด๋‚ด๋ฉด refreshToken๊ณผ accessToken์„ ๋ฆฌํ„ดํ•œ๋‹ค.
  2. POST /silent-refresh: ์ฟ ํ‚ค์— ๋‹ด๊ธด refreshToken์ด ์ž๋™์œผ๋กœ ๋ณด๋‚ด์ง€๋ฉด ์ƒˆ๋กœ์šด refreshToken๊ณผ accessToken์„ ๋ฆฌํ„ดํ•œ๋‹ค.

๋‘ API ๋ชจ๋‘ HTTP ์‘๋‹ต Set-Cookie ํ—ค๋”์— refreshToken ๊ฐ’์„ ์„ค์ •ํ•˜๊ณ  accessToken ์„ JSON payload์— ๋‹ด์•„ ๋ณด๋‚ด์ค˜์•ผ ํ•œ๋‹ค.

ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜๋ฆฌํ•˜๊ธฐ

1. ๋กœ๊ทธ์ธ & 2. ๋กœ๊ทธ์ธ ๋งŒ๋ฃŒ๋˜๊ธฐ ์ „์— ์—ฐ์žฅ

const JWT_EXPIRY_TIME = 24 * 3600 * 1000; // ๋งŒ๋ฃŒ ์‹œ๊ฐ„ (24์‹œ๊ฐ„ ๋ฐ€๋ฆฌ ์ดˆ๋กœ ํ‘œํ˜„)

onLogin = (email, password) => {
    const data = {
        email,
        password,
    };
    axios.post('/login', data)
        .then(onLoginSuccess)
        .catch(error => {
            // ... ์—๋Ÿฌ ์ฒ˜๋ฆฌ
        });
}

onSilentRefresh = () => {
    axios.post('/silent-refresh', data)
        .then(onLoginSuccess)
        .catch(error => {
            // ... ๋กœ๊ทธ์ธ ์‹คํŒจ ์ฒ˜๋ฆฌ
        });
}

onLoginSuccess = response => {
    const { accessToken } = response.data;

    // accessToken ์„ค์ •
    axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;

    // accessToken ๋งŒ๋ฃŒํ•˜๊ธฐ 1๋ถ„ ์ „์— ๋กœ๊ทธ์ธ ์—ฐ์žฅ
    setTimeout(onSilentRefresh, JWT_EXPIRRY_TIME - 60000);
}

3. ํŽ˜์ด์ง€ ๋ฆฌ๋กœ๋“œ ๋  ๋•Œ ๋กœ๊ทธ์ธ ์—ฐ์žฅ

// ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ๋กœ๊ทธ์ธ ์‹œ๋„
class App extends Componet {
    componentDidMount() {
        onSilentRefresh();
    }
    // ...
}

๐Ÿฅณ ๋งˆ์น˜๋ฉฐ

localStorage๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ์–ด๋””์— ์ €์žฅ๋˜๋Š” ๊ฑด์ง€, ์„ธ์…˜๊ณผ JWT์˜ ์ฐจ์ด์ ์ด ๋ญ”์ง€ ์ •๋„๊ฐ€ ๊ถ๊ธˆํ•ด์„œ ๊ฐ€๋ณ๊ฒŒ ๊ตฌ๊ธ€๋ง ํ–ˆ๋Š”๋ฐ, ์ฝ๋‹ค ๋ณด๋‹ˆ ๋ณด์•ˆ ์ทจ์•ฝ์ ์— ๋Œ€ํ•ด์„œ๋„ ์•Œ๊ฒŒ ๋˜๊ณ , ์ธ์ฆ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋ธŒ๋ผ์šฐ์ € ์ €์žฅ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋ฐœ์ƒํ•˜๋Š” ๋ณด์•ˆ ์ด์Šˆ๋„ ๋‹ค์–‘ํ•˜๋‹ค๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ฝ๋Š” ์ˆœ๊ฐ„์—๋Š” ์ดํ•ดํ•œ ๊ฒƒ ๊ฐ™์•˜์ง€๋งŒ ๊ณต๊ฒฉ ์ด์Šˆ๋งˆ๋‹ค ์—ฌ๋Ÿฌ ๋ฐฉ์‹์œผ๋กœ ์ „๊ฐœ๋˜๊ณ , ๋Œ€์‘ ๋ฐฉ์‹์„ ์ •ํ•˜๋ ค๋ฉด ์ „์ฒด ๊ทธ๋ฆผ์„ ์ดํ•ดํ•ด์•ผ ํ•ด์„œ ๋ธ”๋กœ๊น…ํ•˜๋ฉฐ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ ๊ฐ€์žฅ ์•ˆ์ „ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์œ ์ € ์ธ์ฆ ๋ฐฉ์‹์„ ์ œ์•ˆํ•˜๊ณ  ๋งˆ๋ฌด๋ฆฌ ์ง€์—ˆ์ง€๋งŒ, ๋ณด์•ˆ์ด๋ผ๋Š” ์ปค๋‹ค๋ž€ ๋น™์‚ฐ์˜ ์ผ๋ถ€๋ฅผ ๋ฐœ๊ฒฌํ•œ ์ •๋„์ด๋ฆฌ๋ผ. ๋” ๋‚˜์€ ์ธ์ฆ ๋ฐฉ์‹์„ ์•Œ๊ณ  ์žˆ๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ์˜๊ฒฌ์ด ์žˆ๋‹ค๋ฉด ๊ณต์œ ํ•ด ์ฃผ์‹œ๊ธธ. (Please!) ๋˜ ๊ธ€์— ๊ด€๋ จ๋œ ์–ด๋–ค ํ”ผ๋“œ๋ฐฑ์ด๋ผ๋„ ํ™˜์˜์ด๋‹ค. ์ž์œ ๋กญ๊ฒŒ ์–˜๊ธฐํ•ด ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ

profile
๋ฃฐ๋ฃจ ํ”ผ๋“œ๋ฐฑ ํ™˜์˜

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

comment-user-thumbnail
2020๋…„ 7์›” 5์ผ

์™€์šฐ ์ •๋ง ์ข‹์€ ๋‚ด์šฉ์ด๋„ค์š”!!

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2020๋…„ 7์›” 5์ผ

ํด๋ผ์ด์–ธํŠธ ๋ณด์•ˆ๐Ÿ”๋„ ์ •๋ง ์ค‘์š”ํ•œ๋ฐ... ์•„๋‚Œ์—†์ด ๊ณต์œ ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ™‡

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2020๋…„ 7์›” 6์ผ

์œ ์šฉํ•œ ์ •๋ณด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์ €๋„ ๊ฐœ๋ฐœํ•˜๋Š” ํ”„๋กœ์ ํŠธ์— ์ž˜ ์ฐธ๊ณ ํ•ด๋ณด๋ ค๊ณ  ํ•ด์š”!!

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2020๋…„ 7์›” 6์ผ

์˜ค์˜ค ๋ฉ‹์ ธ์š”

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

์ •๋ง ์ข‹์€ ๋‚ด์šฉ์ด๋„ค์š”!! ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2020๋…„ 7์›” 10์ผ

์•Œ๋ ค์ฃผ์‹  ๋ฐฉ๋ฒ•๋Œ€๋กœ ๊ตฌํ˜„ํ•ด ๋ณด๊ณ  ์žˆ๋Š” ์ดˆ๋ณด ๊ฐœ๋ฐœ์ž ์ž…๋‹ˆ๋‹ค.
๋ช‡๊ฐ€์ง€ ๊ถ๊ธˆํ•ด์„œ ์—ฌ์ญ™๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

  1. axios.defaults.headers.common['Authorization']์˜ ๊ฐ’์ด ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์˜ฎ๊ฒจ๊ฐˆ๋•Œ ์œ ์ง€๊ฐ€ ์•ˆ๋˜๋Š”๋ฐ, ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜์‹œ๋‚˜์š”? ์ž‘์„ฑํ•˜์‹  ํฌ์ŠคํŠธ์—์„œ๋Š” ๋ณด์•ˆ์ƒ ์ด์œ ๋กœ ๋ฆฌ๋•์Šค๋‚˜ ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์•ˆ์จ์•ผํ•œ๋‹ค๊ณ  ํ•˜์‹œ๋Š” ๊ฒƒ์œผ๋กœ ์ดํ•ดํ–ˆ๋Š”๋ฐ์š”. (์ผ๋‹จ์€ ๋งค๋ฒˆ accessํ† ํฐ์„ ์ƒˆ๋กœ ๋ฐœ๊ธ‰ํ•ด์ค˜์•ผ ํ•œ๋‹ค๊ณ  ์ดํ•ด๋Š” ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด csrf ํ† ํฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋˜๋‹ˆ๊นŒ csrf ํ† ํฐ์€ ๋”ฐ๋กœ ์•ˆํ•ด๋„ ๋ ๊นŒ์š”?)

  2. ๋ฐฑ์—”๋“œ๋Š” ์–ด๋–ค์ ˆ์ฐจ๋กœ ์ž‘๋™ํ•˜๋„๋ก ๊ตฌ์„ฑํ•ด์•ผ ํ• ๊นŒ์š”? ์ž‘์„ฑํ•˜์‹  ํฌ์ŠคํŠธ์—์„œ๋Š” json์œผ๋กœ access ํ† ํฐ์„ ๋ณด๋‚ด์ฃผ์–ด์•ผ ํ•œ๋‹ค๊ณ  ํ•˜์…จ์œผ๋‹ˆ ํŽ˜์ด์ง€๊ฐ€ ์ด๋™๋˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจ ๋  ๋•Œ๋งˆ๋‹ค ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•˜๋Š” api๋ฅผ ๋งŒ๋“ค์–ด์„œ refresh ํ† ํฐ์„ ์ฟ ํ‚ค์—์„œ ์ฝ์–ด์™€์„œ ๊ทธ์—๋”ฐ๋ผ ๊ฐ’์„ access ํ† ํฐ์„ ์ƒˆ๋กœ ๋ฐœ๊ธ‰ํ•ด์ฃผ๋„๋ก ํ•ด์•ผ ํ• ๊นŒ์š”?

access ํ† ํฐ๊ณผ refresh ํ† ํฐ ์‚ฌ์šฉ์˜ˆ๊ฐ€ ๋งŽ์ด ์—†์–ด์„œ ํ˜ผ์ž ๋…ํ•™ํ•ด์„œ ๋งŒ๋“œ๋Š๋ผ ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์ด ์žˆ์—ˆ๋Š”๋ฐ ์ข‹์€ ๋‚ด์šฉ ๊ณต์œ  ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

2๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2020๋…„ 7์›” 18์ผ

์˜ค ์ฝ๊ธฐ ์ข‹๊ฒŒ ์ •๋ฆฌํ•ด์ฃผ์…จ๋„ค์š” ใ…Žใ…Ž ์ข‹์€ ๊ธ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค~!

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2020๋…„ 7์›” 18์ผ

@yaytomato ๋‹˜ ์ถ”๊ฐ€์ ์ธ ์˜๊ฒฌ์„ ๋“œ๋ฆฌ๊ณ ์ž ๋Œ“๊ธ€๋‹ฌ์•„๋ด…๋‹ˆ๋‹ค! ์ด๊ธ€์„ ์ฝ๋Š” ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž ๋ถ„๋“ค๋„ ํ•œ๋ฒˆ ๊ณ ๋ฏผํ•ด ๋ณด์‹œ๋ฉด ์ข‹์„๊ฑฐ ๊ฐ™์Šต๋‹ˆ๋‹ค ~ ๊ทธ๋ฆฌ๊ณ  ๋ฒจ๋กœ๊ทธ ํŠธ๋ Œ๋”ฉ์— ๋‚˜์˜ค์‹ ๊ฑฐ ์ถ•ํ•˜๋“œ๋ฆฝ๋‹ˆ๋‹ค !!

ํ† ํฐ ๋ฐฉ์‹์€ ์ •๋ง ์•ˆ์ „ํ•œ๊ฐ€? Refresh token ์ด ํƒˆ์ทจ๋ฅผ ๋‹นํ•˜๋ฉด ์–ด๋–ป๊ฒŒ๋˜์ง€ ?

๋ณธ๋ฌธ์˜ ๋‚ด์šฉ์„ ๋‹ค์‹œ ์š”์•ฝํ•˜๋ฉด
์ตœ์ดˆ ๋กœ๊ทธ์ธ์‹œ์— access token/ refresh token์„ ์ฃผ๊ณ  ํ‰์†Œ์—” access token ์œผ๋กœ API๋ฅผ ์ด์šฉํ•˜๋‹ค๊ฐ€ ๋งŒ๋ฃŒ๋˜๋ฉด refresh token ์œผ๋กœ ๋‹ค์‹œ access token์„ ๋ฐœ๊ธ‰ ๋ฐ›๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ์ด๋•Œ refresh token ์€ ์ฟ ํ‚ค์— ์ €์žฅํ•œ๋‹ค๊ณ  ํ•˜์…จ์Šต๋‹ˆ๋‹ค.

์ด ์ƒํ™ฉ์—์„œ ์ „์ œ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด refresh๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๊ด€ํ•˜๋Š” ๊ฒƒ์ธ๋ฐ ์›น๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ์‚ฌ์‹ค์ƒ ์ด ํ† ํฐ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€ํ•  ๊ณณ์€ ์—†์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ €๋Š” ์•„์ง ์›น์—์„œ๋Š” JWT๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. <-- ์ œ ๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ (Refresh token์„ ํด๋ผ์ด์–ธํŠธ์˜ ๋กœ์ปฌ์—์„œ ๊ฐ•๋ ฅํ•˜๊ฒŒ ์ง€ํ‚ค๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค๋ฉด ์ถ”๊ฐ€์ ์œผ๋กœ ๋Œ“๊ธ€ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค ~)

๊ทธ๋Ÿผ ๋ญ์•ผ... ํ† ํฐ์ธ์ฆ์€ ๊ฒฐ๊ตญ ๋ณด์•ˆ์ด ์•ˆ๋˜๋Š”๊ฑด๊ฐ€? https ๋กœ ํ•˜๋ฉด ๋‹ค ๋๋‚˜๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€? ์ด๋Ÿฐ ์ƒ๊ฐ์„ ํ•˜์‹ค ์ˆ˜ ์žˆ๋Š”๋ฐ
์•„๋‹™๋‹ˆ๋‹ค. ์ €๋„ ํ•œ์ฐธ ๋ถ€์กฑํ•˜์ง€๋งŒ ์ œ๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฑ™๊ฒจ์•ผํ•  ๋ณด์•ˆ์€ ์นจํ•ด ์‹œ๊ฐ„์„ ์งง๊ฒŒ ํ•˜๋Š”๊ฒŒ ํ•˜๋Š”๊ฒƒ ์ž…๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ํ”ผํ•ด๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด์ง€์š”~

"๋ณด์•ˆ์— ์€์ด์•Œ์€ ์—†๋‹ค"

๋” ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ์ง€ํ‚ค๊ณ  ๋ณด์•ˆ์— ๊ด€์‹ฌ์„ ๊ฐ€์ง€๊ณ  ์•ˆ์ „์žฅ์น˜๋ฅผ ํ•˜๋Š” ๊ฒƒ์€ ์ •๋ง์ •๋ง ์ค‘์š”ํ•œ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

2๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2020๋…„ 7์›” 18์ผ

์ž˜ ์ •๋ฆฌ๋œ ๊ธ€์ด๋„ค์š”! ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :)

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 7์›” 29์ผ

์ข‹์€ ์ •๋ณด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 8์›” 6์ผ

๊ฐœ์ธ์ ์œผ๋กœ๋Š” xss์— ์ทจ์•ฝํ•œ๋ฐฉ์‹์€ ์ „๋ถ€ ์ œ๊ฑฐํ•˜๊ณ  cookie๋กœ ์ „๋ถ€ ์ €์žฅํ•œ๋‹ค์Œ
csrf token์„ jwtํ† ํฐ์— ํฌํ•จ์‹œํ‚ค๋Š”๊ฑธ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ”„๋ก ํŠธ์—์„œ๋Š” jwtํ† ํฐ์— ํฌํ•จ๋œ csrf ํ† ํฐ์„ ํ—ค๋”์— ๋„ฃ์–ด์„œ ๋ณด๋‚ด๊ณ 
์„œ๋ฒ„์—์„œ๋Š” jwt ํ† ํฐ ๋‚ด csrf์™€ ํ—ค๋”๋กœ ๋“ค์–ด์˜จ csrf์˜ ๊ฐ’๋น„๊ต๋ฅผ ํ†ตํ•ด csrf๋ฐฉ์–ด๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.
์ง€๊ธˆ ์•Œ๋ ค์ฃผ์‹œ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ๊ฒฐ๊ตญ xss์˜ ์ทจ์•ฝ์ ์ด ๋‚จ์Šต๋‹ˆ๋‹ค.

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 8์›” 11์ผ

์ €๋Š” ๋ฆฌํ”„๋ ˆ์‰ฌํ† ํฐ๊ณผ ์—‘์„ธ์Šค ํ† ํฐ์„ ๋ชจ๋‘ ๋กœ์ปฌ์Šคํ† ๋ฆฌ์ง€์— ๋„ฃ๊ณ , ajax call์‹œ ์—‘์„ธ์Šคํ† ํฐ์„ ํ—ค๋”์— ์ „๋‹ฌํ•˜๋Š” ์‹์œผ๋กœ ํ•˜๊ณ  expired๋˜๋ฉด ์ž๋™์œผ๋กœ ์—ฐ์žฅํ•ด์ฃผ๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด ๊ฒฝ์šฐ์—๋„ ๊ดœ์ฐฎ์ง€ ์•Š์„๊นŒ์š”?

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 8์›” 16์ผ

์ •๋ง ์ €๊ฐ™์€ ๋‰ด๋น„ํ•œํ…Œ ๋„ˆ๋ฌด๋„ˆ๋ฌด ์ข‹์€ ์ •๋ณด๊ธ€์ด๋„ค์š”,.... ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๊ณต๋ถ€ํ•˜๊ณ ์‹ถ์–ด์ง€๋„ค์šฅ

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 8์›” 19์ผ

์ข‹์€ ๋‚ด์šฉ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 8์›” 23์ผ

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘์— ๋กœ๊ทธ์ธ ๋ณด์•ˆ ๊ด€๋ จ ํ•ด๊ฒฐ๋ฒ•์„ ์ฐพ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ ๋„ˆ๋ฌด๋‚˜ ์ข‹์€ ํฌ์ŠคํŠธ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค !!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 8์›” 24์ผ

๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ๊ฑฐ์ง 1์ฃผ์ผ์€ jwt, ์‹œํ์–ด ์ฝ”๋”ฉ, ๋ชจ์˜ํ•ดํ‚น ์ด๋Ÿฐ๊ฑฐ ์ฐพ๋‹ค ์ด ๊ธ€์„ ๋ฐœ๊ฒฌํ–ˆ๋„ค์š”. ์ฐพ๋‹ค๋ณด๋ฉด ํ•ญ์ƒ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ํด๋ผ์ด์–ธํŠธ์— ์–ด๋–ป๊ฒŒ ๋ณด๊ด€ํ•ด์•ผํ•˜๋Š” ์ด์Šˆ์— ๋Œ€ํ•œ ๋‹ต์„ ๋ชป ์ฐพ์•„์„œ ์—ฌ๊ธฐ์ €๊ธฐ ๋‹ค๋…”์Šต๋‹ˆ๋‹ค. https only๋‚˜ secure๋‚˜ ๋„ฃ์„๊บผ๋ฉด ์—‘์„ธ์Šค ํ† ํฐ์„ ์ €๊ธฐ ๋‹ค ๋„ฃ์ง€.
๊ตณ์ด ๋ฆฌํ”„๋ ˆ์‹œ๋ฅผ ์จ์•ผํ•˜๋Š” ์˜๋ฌธ๋„ ์ƒ๊ธฐ๊ณ .(์ €๋ ‡๊ฒŒ ๊นŒ์ง€ ํ•ด๋„ ์ฟ ํ‚ค ํ„ธ ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ์ž๋ฉด ๋ฆฌํ”„๋ ˆ์‹œ๋Š” ๊ทธ๋ƒฅ ํ„ธ๋ฆฌ๋Š”๊ฑฐ๋‹ˆ๊นŒ์š”.)
์–ด์งœํ”ผ ํ„ธ๋ฆด ์ˆ˜ ์žˆ๋Š”๊ฑฐ ์„œ๋ฒ„์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ํ•ด์ค˜์•ผ ํ•˜๋‚˜ ๊ณ ๋ฏผ๊ณ ๋ฏผ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.
๋ณด์•ˆ๊ณผ ํŽธ์˜์„ฑ&์„ฑ๋Šฅ์€ ์–ธ์ œ๋‚˜ ๋ฐ˜๋น„๋ก€ํ•˜๋Š” ๊ฑฐ๋ผ ์ ์ ˆํ•œ ์„ ์—์„œ ํƒ€ํ˜‘์„ ๋ด์•ผ ํ•˜๋”๋ผ๊ตฌ์š”.
์ด ๊ธ€ ์ฝ๊ณ  ๋งŽ์ด ์ƒ๊ฐ์ด ์ •๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ณ ๋ง™์Šต๋‹ˆ๋‹ค.

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 8์›” 25์ผ

๊น”๋”ํ•œ ์ •๋ฆฌ์™€ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ์„ค๋ช… ๋•๋ถ„์— ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉฐ ๋งŽ์€ ์ฐธ๊ณ ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!!!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 9์›” 1์ผ

์ข‹์€ ๋‚ด์šฉ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 9์›” 2์ผ

์ข‹์€ ๋‚ด์šฉ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2020๋…„ 9์›” 8์ผ

๐Ÿ‘ ์ข‹์€ ๊ธ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :)

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
์•ฝ 2์‹œ๊ฐ„ ์ „

์™€๋“œ๋ฐ•๊ณ  ๊ฐ‘๋‹ˆ๋‹น

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ