๋ก๊ทธ์ธ ์ ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ JWT์ ํตํด์ ํด๋ผ์ด์ธํธ์์ ๊ด๋ฆฌํ๊ธฐ๋ก ํ์๋ค.
๋ก๊ทธ์ธ ์ ์๋ฒ๋ก๋ถํฐ ์๋ต ๋ฐ๋๋ฅผ ํตํด ์ก์ธ์ค ํ ํฐ์ ์ ๋ฌ๋ฐ๊ณ , ์๋ต ํค๋(Set-Cookie) ๋ฅผ ํตํด ์ฟ ํค์ ๋ฆฌํ๋ ์ฌ ํ ํฐ์ ๋ด์ ์ ๋ฌ๋ฐ์๋ค.
๊ทธ ์ด์ ๋ JWT๋ ๊ธฐ๋ณธ์ ์ผ๋กStateless
ํ ํน์ฑ์ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ์๋ฒ๊ฐ ๋ณ๋์ ์ํ๋ฅผ ์ ์งํ์ง ์๋๋ฐ ์ด๋ ํ ํฐ์ด ํ์ทจ๋ ๊ฒฝ์ฐ ์ด๋ฅผ ๋ฌดํจํํ๊ฑฐ๋ ๋ณ๊ฒฝ ์ํ๋ฅผ ๋ฐ์ํ๊ธฐ ์ด๋ ต๋ค๋ ๋ฌธ์ ์ ์ด ์์ต๋๋ค. ์ด๋ฐ ๋ฌธ์ ์ ์ ๋ณด์ํ๊ธฐ ์ํด ์ ์ ํ๊ฒtrade-off
ํ์ฌ ์ผ๋ถ๋ถ์ Statefulํ๊ฒ ๊ด๋ฆฌํ์ฌ๋ณด์์ฑ
์ ๋์์ต๋๋ค.
JWT๋ฅผStateful
ํ๊ฒ ๊ด๋ฆฌํ๋ฉด ์๋ฒ๊ฐ ํ ํฐ์ ์ ํจ์ฑ์ ์ฃผ๊ธฐ์ ์ผ๋ก ๊ฒ์ฆํ๊ณ , ํ์ ์ ํ ํฐ์ ๋ฌดํจํ ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค๋ฉด, ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์์ํ๊ฑฐ๋ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ณ๊ฒฝํ ๋ ํด๋น ํ ํฐ์ ์ฆ์ ํ๊ธฐํ ์ ์์ต๋๋ค.
JWT(Json Web Token) ์นํ ํฐ์ด๋ผ ๋ถ๋ฅด๋ฉฐ, ์ ์ ์๋ช ๋ URL-safe์ JSON์ ๋๋ค.
JWT ํ ํฐ ์ธ์ฆ๋ฐฉ์์ ๋น๋ฐํค(๊ฐ์ธํค or ๋์นญํค)๋ก ์ํธํํ๋ฉฐ ์ด๋ JWT ํ ํฐ์ ์ฌ์ฉํด์ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ํต์ ์ ์์ ํ๊ฒ ํด์ค๋๋ค.
JWT๋ .์ ๊ธฐ์ค์ผ๋ก header, payload, signature๋ก ์ธ ํํธ๋ก ๋๋ฉ๋๋ค.
ํค๋(Header)์๋ JWT๋ฅผ ์ด๋ป๊ฒ ๊ฒ์ฆ(์๋ช ์์ฑ)ํ๋์ง์ ๋ํ ๋ด์ฉ์ธ ํ ํฐ์ ํ์ , ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ด ์ด๋ค ์๊ณ ๋ฆฌ์ฆ์ธ์ง์ ๋ํ ์ ๋ณด๊ฐ ๋ค์ด์์ต๋๋ค.
ํ์ด๋ก๋(Payload)์๋ ํ ํฐ์ด ์ ๋ฌํ๋ ํด๋ ์ or ๋ฐ์ดํฐ๊ฐ ํฌํจ๋ฉ๋๋ค. ์ด๋ key - value์ ํ ์์ผ๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉฐ ์ฌ๋ฌ ๊ฐ์ ํด๋ ์์ ๋ด์ ์ ์๊ณ , ํด๋ ์์ public ํน์ privateํ ๊ฑด์ง ๋ฑ๋กํ ๊ฒ์ธ์ง ๊ฒฐ์ ํ ์ ์์ต๋๋ค.
์๋ช (Signature)์ ํค๋(header)์ ํ์ด๋ก๋(Paylaod)๋ฅผ ํฉ์น ๋ฌธ์์ด์ ์๋ช ํ ๊ฐ์ด๋ฉฐ ์ ์ก ์ค์ ํ ํฐ์ด ๋ณ์กฐ๋์ง ์์๋์ง ํ์ธํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
JWT ํ ํฐ์ ํฐ ๋ฌธ์ ์ ์ ํ์ทจ ๋นํ์ ๋ ์๋ฒ๊ฐ ํ์ทจํ ์ฌ๋์ ์ ๋ขฐํ ๋งํ ์ฌ๋์ธ์ง ๊ตฌ๋ณ์ ๋ชปํด์ ํ์ทจ์๊ฐ ์ ๋ขฐ ๊ฐ๋ฅํ ์ฌ๋์ธ ๊ฒ์ฒ๋ผ ์ธ์ฆํ ์ ์๊ธฐ ๋๋ฌธ์ ์ฃผ์ ๋ฌธ์ ์ ์ผ๋ก ์ ๊ผฝํ๋๋ค.
๊ทธ๋์ ์ ํจ๊ธฐ๊ฐ์ ์งง๊ฒ ๋๋ฉด ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ ์์ฃผํด์ผํ๋ ๋ฌธ์ ์ ์ด ์๊ฒจ์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ธ ์ธก๋ฉด์์ ์ข์ง ์๊ณ , ๊ธฐ๊ฐ์ ๊ธธ๊ฒ ๋๋ฉด ๋ณด์์ ํ์ทจ ์ํ์ด ๋์์ง๋๋ค.
๊ทธ๋์ ๊ตฌ์กฐ์ ์ธ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด Access Token๊ณผ Refresh Token๋ก JWT ํ ํฐ์ 2๊ฐ๋ก ๋ก๋๋ค.
๋ณดํต Access Token์ ์ ํจ๊ธฐ๊ฐ์ ์งง๊ฒ, Refresh Token์ ์ ํจ๊ธฐ๊ฐ์ ๊ธธ๊ฒ ์ค์ ํด์ฃผ์ด์ ํ์์ API ํต์ ํ ๋๋ Access Token์ ์ฌ์ฉํ๊ณ , Refresh Token์ Access Token์ด ๋ง๋ฃ๋์ด ๊ฐฑ์ ๋ ๋๋ง ์ฌ์ฉํ๊ฒ ํด์ค๋๋ค.
๋ธ๋ผ์ฐ์ ๋ ์ก์ธ์ค ํ ํฐ์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๊ณ , ๋ฆฌํ๋ ์ ํ ํฐ์ ์ฟ ํค์ ์ ์ฅํ๋ ์ด์ ๋ ์ก์ธ์ค๋ ์งง๊ฒ, ๋ฆฌํ๋ ์๋ ๊ธธ๊ฒ ์ ์ฅํ์ฌ ๋ณด์ ํจ์จ์ ์ฌ๋ ค์ฃผ๊ธฐ ๋๋ฌธ์ ๋๋ค.(๋ํ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๋ ๊ฒ๋ณด๋ค ์ฟ ํค์ ์ ์ฅํ๋๊ฒ์ด ์๋์ ์ผ๋ก ๋ณด์์ฑ์ด ๋ฐ์ด๋๊ณ http-only๋ฅผ ๋ฆฌํ๋ ์์ ๊ฑธ์ด์ฃผ๋ฉด http ํต์ ์ ์๋ํ ๋ ์๋ฒ์ ๋ฆฌํ๋ ์ ํ ํฐ์ ์ ์กํด์ฃผ๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ์์์ js ์ ๊ทผ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.)
์ด๋ ์ก์ธ์ค ํ ํฐ์ APIํธ์ถ์ด๋ ์๊ณ ๊ณ ์นจ, ํ์ด์ง ์ด๋ ์ ํ ํฐ ๋ง๋ฃ ์ฌ๋ถ๋ฅผ ์ฒดํฌํด์ ์ ํจํ๋ค๋ฉด ์คํ๋๋๋กํ๊ณ ๋ฆฌํ๋ ์ ํ ํฐ์ ์ก์ธ์ค ํ ํฐ์ ์ฌ๋ฐ๊ธํด์ฃผ๋ ์ฉ๋๋ก ์ฌ์ฉํ๋ค.
๋ฆฌํ๋ ์ ํ ํฐ์ด ์ ํจํ๋ค๋ฉด ์ก์ธ์ค ํ ํฐ์ ์ฌ๋ฐ๊ธํด์ฃผ๋ ์ญํ ์ด๊ธฐ ๋๋ฌธ์ ์๋ฒ์์ ์ ํด๋ ์๊ฐ์ด ๋ค๋๋ฉด ์ฟ ํค์์ ์ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ ๊ทธ๊ฑธ ๊ฐ์ง๊ณ ๋ก๊ทธ์์ ์์ผฐ๋ค.
์ด๋ ์ด๋ ๊ฒ ์ดํดํ ์ด์ ๋ ๋ฆฌํ๋ ์ ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด ์ก์ธ์ค ํ ํฐ์ ์ฌ๋ฐ๊ธ ๋ชปํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋ฆฌํ๋ ์๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ก๊ทธ์ธ, ๋ก๊ทธ์์ ์ํ๋ฅผ ๊ตฌํํ๋ฉด ๋๊ฒ ๊ตฌ๋๋ผ๊ณ
์๊ฐ์ ํ๊ณ ๋ ๋ค๋ฅธ ์ด์ ๋ก๋ ๋ฆฌํ๋ ์ ํ ํฐ์ ์ฟ ํค์ ์ ์ฅํ์ ๋ ๋ฆฌํ๋ ์ ํ ํฐ์ด ์๋ฒ์์ ์ ๊ณตํ ๋ฆฌํ๋ ์ ํ ํฐ์ด ์๊ฐ์ด ๋ง๋ฃ๋๋ฉด ์ฟ ํค์์ ์ญ์ ๋๊ธฐ ๋๋ฌธ์ ์! ๋ฆฌํ๋ ์ ํ ํฐ์ด ๋ง๋ฃ๋์ ์ฟ ํค์์ ์ญ์ ๋๋ฉด ์๋ ๋ก๊ทธ์์ ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๊ตฌํํ๋ฉด ๋๊ฒ ๊ตฌ๋! ์ด๋ ๊ฒ ์ดํดํ๋ค. ํ์ง๋ง ๋ฆฌํ๋ ์ ํ ํฐ์ ์ค๋ก์ง ์ก์ธ์ค ํ ํฐ์ ์ฌ๋ฐ๊ธ ํด์ฃผ๋ ์ฉ๋์ด๊ธฐ ๋๋ฌธ์ ๊ทธ๋ ๊ฒ ํ๋ฉด ์๋๋ค๊ณ ํ๋ค.
๋ํ ์ฟ ํค์ ๋ฆฌํ๋ ์ ํ ํฐ์ ์ ์ฅํ๊ฒ ๋๋ฉด ์๊ธฐ๋ ๋ฌธ์ ์ ์ผ๋ก
1. ๋ก์ปฌ ํ๊ฒฝ์์ ์ฟ ํค์ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ํฌ๋กฌ ๋ณด์ ์ ์ฑ
๋๋ฌธ์ ์ ์ก์ด ์๋๋ค.
2. Cross Site Scripting(XSS) ๊ณต๊ฒฉ์ ์ทจ์ฝํ๋ค.
๊ทธ๋์ ์๋ฒ์์๋ ์ฟ ํค์ js๋ก ์ ๊ทผํ์ง ๋ชปํ๊ฒํ๋ http-only๋ฅผ ๊ฑธ์ด์ค์ ํด๋ผ์ด์ธํธ์์ http ์์ฒญ์ด ๋ฐ์ํ๋ฉด ์๋์ผ๋ก ์๋ฒ๋ก ์ฟ ํค ๊ฐ์ ์ ๋ฌํ๋๋กํ์ฌ ๋ก์ปฌ ํ๊ฒฝ์์ ์ฟ ํค ๊ฐ์ ์ ์กํ ์ ์๊ณ , ๋ํ XSS ๊ณต๊ฒฉ์ ๋๋นํ ์ ์๋ ์ฅ์ ์ด ์๋ค.
์ด๋ก์จ ๋ธ๋ผ์ฐ์ ์์๋ ์ฟ ํค์ ์ ๊ทผ์ด ๋ถ๊ฐ๋ฅํด์ก๊ธฐ ๋๋ฌธ์ ๋์ฑ ๋ ๋ก๊ทธ์ธ ์ํ ๊ด๋ฆฌ๋ฅผ ๋ฆฌํ๋ ์ ํ ํฐ์ ๊ฐ์ง๊ณ ๊ตฌํํ๋ฉด ์๋๊ฒ ๋์์ต๋๋ค.
๊ทธ๋ฐ๋ฐ ๋ก์ปฌ ํ๊ฒฝ์ ํ๊ณ ๋๋ฌธ์ ์ฟ ํค๋ฅผ ์ ์งํ์ง ๋ชปํ๋ ์๋ฌ๋ค์ ๋ฐ์๋์์ผ๋ฉฐ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
CSRF ๊ณต๊ฒฉ
Cross Site Request Forgery (์ดํ CSRF) ๊ณต๊ฒฉ์ ์น์ฌ์ดํธ ์์์์ ์์ฒญ์ ์์กฐํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
์ด๊ฒ์ ๋ง๋ ์ต์ ์ผ๋ก SameSite๋ผ๋ ์ต์ ์ด ์ด 3๊ฐ์ง๊ฐ ์กด์ฌํฉ๋๋ค.
Lax : ํฌ๋ก์ค ์ฌ์ดํธ ์์ฒญ์ ํ์ฉ ํ์ง๋ง get ์์ฒญ๋ง ์ฌ์ฉ๊ฐ๋ฅํ๋ ๋ํดํธ ๊ฐ์
๋๋ค.(post๋ ์ฌ์ฉ ๋ถ๊ฐ๋ฅ) - ๊ธฐ๋ณธ๊ฐ
(๋ค๋ฅธ ๋๋ฉ์ธ์์ ์์ฒญํ ๋ ์ฟ ํค๊ฐ ์๋ ์ ์ก๋์ง ์์ต๋๋ค.)
None : ๋๋ฉ์ธ ๊ฒ์ฆ X ์ด๋์๋ ์ฌ์ฉ ๊ฐ๋ฅํ๊ณ secure = ture ํ์(ํด๋น ์ค๋ฅ๋ฅผ ํด๊ฒฐํ๋ ๊ณผ์ ์์ https ํ์)
Strict : ๊ฐ์ ๋๋ฉ์ธ์์๋ง ์ฟ ํค ์ ์ก ๊ฐ๋ฅ
์ก์ธ์ค ํ ํฐ์ ๊ธฐ์ค์ผ๋ก ๋ก๊ทธ์ธ ์ํ๋ฅผ ๊ด๋ฆฌํด์ ๋ง๋ฃ ์๊ฐ 1๋ถ ์ ์ ์ฌ๋ฐ๊ธ์ ๋ฐ๊ณ ๋ฆฌํ๋ ์ ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด ์ก์ธ์ค ํ ํฐ์ ์ฌ๋ฐ๊ธ ๋ชป ๋ฐ๊ธฐ ๋๋ฌธ์ ๋ก๊ทธ์์์ด ๋๋ ํ์์ผ๋ก ๊ตฌํํ ๋ ค๊ณ ํ์ง๋ง... ์ก์ธ์ค ํ ํฐ ์ฌ๋ฐ๊ธ api๋ฅผ ํธ์ถ ํ ๋ ์ก์ธ์ค ํ ํฐ์ ๋ง๋ฃ์๊ฐ์ด ๋จ์ ์๋ค๋ฉด
ํ ํฐ ์ค๋ณต๊ณผ ๋ณด์์์ ์ด์ ๋ก ์๋ฒ์์ ๋ง์๋จ๊ธฐ ๋๋ฌธ์ ์ฌ๋ฐ๊ธ์ด ์๋๊ณ 401์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋ํ ๋ง๋ฃ๊ฐ ๋ ํ ์ฌ๋ฐ๊ธ api๋ฅผ ํธ์ถํ ๋ ค๊ณ ํ๋ ๋ก๊ทธ์์ ๊ธฐ๋ฅ ๋ก์ง ๋๋ฌธ์ ๋ก๊ทธ์์์ด ๋ฐ์ํ๋ ๋จ์ ์ด ๋ฐ์ํ๋ค.
๊ฒฐ๊ตญ ๋๊ณ ๋์์ ๋ก๊ทธ์ธ ์ํ ๊ด๋ฆฌ๋ ๋ฆฌํ๋ ์ ํ ํฐ์ผ๋ก ํด์ค์ผ ๊น๋ํ๊ฑฐ ๊ฐ์ต๋๋ค..(http-only๋ฅผ ์ฌ์ฉํ๋ฉด js๋ก ์ ๊ทผ์ด ๋ถ๊ฐ๋ฅํ๋ค๊ณ ํด์ ์ฟ ํค์์ ์ฌ๋ผ์ง๋ ์ค ์์๋๋ฐ ์ด๋ sameSite=none, secure=false ์ค์ ์ผ๋ก ์ธํด ์ฟ ํค ์ฐจ๋จ์ด ๋ฐ์ํด์ ์ฟ ํค์์ ์ฌ๋ผ์ง๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด๋ค. ๊ทธ๋์ hyyp-only๋ฅผ ์ฌ์ฉํ ๋ฆฌํ๋ ์ ํ ํฐ์ผ๋ก ๋ก๊ทธ์ธ ์ํ ๊ด๋ฆฌ๋ฅผ ๋ชปํ๋ ์ค๋ก ์ฐฉ๊ฐ์ ํ์ต๋๋ค...)
๊ฒฐ๊ตญ ๋ง์ ์ํ์ฐฉ์ค๋ฅผ ๊ฒช์ด์ ํด๊ฒฐํ์ ๋ ๊นจ๋ฌ์ ์ ์ผ๋ก๋ ์ผ๋จ ์ด๋ก ์์ผ๋ก ์ก์ธ์ค ํ ํฐ ์ฌ๋ฐ๊ธ์ api๋ฅผ ํธ์ถ ํ ๋๋ง๋ค ๋ง๋ฃ๋์๋์ง ํ์ธ ํ ๋ง๋ฃ๊ฐ ์๋์๋ค๋ฉด ํธ์ถํ api ์ํ or ๋ง๋ฃ๊ฐ ๋์๋ค๋ฉด ์ฌ๋ฐ๊ธ api๋ฅผ ํธ์ถํ์ฌ ์๋ก์ด ์ก์ธ์ค ํ ํฐ์ ๋ฐ๊ธํ๋ ์์ผ๋ก ์๊ฐํ์ต๋๋ค. ๊ทธ๋ ๊ฒํ๋ค๋ฉด ๊ธฐ์กด์ ์๋ ์ก์ธ์ค ํ ํฐ์ด ๋ง๋ฃ๋๊ณ api๋ฅผ ํธ์ถํ๋ ์์
์ด ์ด๋ฃจ์ด์ง๋ ์ฌ์ด ์๊ฐ์ด ๋ถ ๋ ์ ๋ก๊ทธ์์์ด ๋ ๊ฑฐ๋ผ๊ณ ์๊ฐ
์ ํ์ต๋๋ค.
ํ์ง๋ง ์ก์ธ์ค ํ ํฐ ๋ง๋ฃ์๊ฐ๊ณผ ํ์ฌ์๊ฐ์ ๋น๊ตํด์ ๋ง๋ฃ๊ฐ ๋๋ฉด ์ฆ์ ์ก์ธ์ค ํ ํฐ์ ์ฌ๋ฐ๊ธํ๋๋ก ๊ตฌํํด๋ณด๋ ์๊ฐ๋ณด๋ค ๋น ๋ฅด๊ฒ ์๋ก์ด ์ก์ธ์ค ํ ํฐ๊ณผ ๋ง๋ฃ์๊ฐ์ ์ ๊ณตํด์ฃผ์์ต๋๋ค.
import { useEffect, useCallback } from "react";
import { useRecoilState } from "recoil";
import { userState, isLoggedInState } from "@/recoil/userState";
import { useLogout } from "@/hooks/useLogout";
import { basicAxios } from "@/api/axios";
import axios from "axios";
const useAuth = () => {
const [user] = useRecoilState(userState);
const [isLoggedIn] = useRecoilState(isLoggedInState);
const { logout } = useLogout();
const refreshAccessToken = useCallback(async () => {
try {
console.log("Starting token refresh...");
const response = await basicAxios.post("/users/refresh");
console.log("Refresh response:", response);
const { accessToken } = response.data.result;
const { value, expiration } = accessToken;
console.log("Access Token: ", { value, expiration });
localStorage.setItem("AccessToken", value);
localStorage.setItem(
"AccessTokenExpiration",
new Date(expiration).getTime().toString()
);
return value;
} catch (error) {
console.error("์ก์ธ์ค ํ ํฐ ๊ฐฑ์ ์คํจ:", error);
if (axios.isAxiosError(error)) {
const response = error.response;
console.log("Axios ์ค๋ฅ ์๋ต:", response);
if (response?.status === 401) {
console.log("Unauthorized ์ค๋ฅ ์๋ต:", response.data);
}
}
return null;
}
}, []);
const checkAndRefreshToken = useCallback(async () => {
const accessToken = localStorage.getItem("AccessToken");
const accessTokenExpiration = localStorage.getItem("AccessTokenExpiration");
if (accessToken && accessTokenExpiration) {
const now = new Date().getTime();
const expirationTime = Number(accessTokenExpiration);
if (now >= expirationTime) {
// ์ก์ธ์ค ํ ํฐ์ด ๋ง๋ฃ๋์์ต๋๋ค. ์๋ก๊ณ ์นจํ์ธ์.
const newAccessToken = await refreshAccessToken();
if (newAccessToken) {
// ์๋ก์ด ์ก์ธ์ค ํ ํฐ ๋ฐ ๋ง๋ฃ ์๊ฐ์ผ๋ก localStorage ์
๋ฐ์ดํธ
localStorage.setItem("AccessToken", newAccessToken);
// axios ์ธ์คํด์ค๊ฐ Authorization ํค๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ํค๋ ์
๋ฐ์ดํธ -> ์๋ก์ด ์ก์ธ์ค ํ ํฐ ํค๋์ ์ ์ฅ
basicAxios.defaults.headers.common[
"Authorization"
] = `Bearer ${newAccessToken}`;
} else {
// ํ ํฐ ์๋ก ๊ณ ์นจ ์คํจ ์ฒ๋ฆฌ, ์ฌ์ฉ์ ๋ก๊ทธ์์ ๊ฐ๋ฅ
logout(); // ์ด ๋ถ๋ถ์์ ํ ํฐ ๊ฐฑ์ ์คํจ ์ ๋ก๊ทธ์์์ ํธ์ถํฉ๋๋ค
}
} else {
// ์ก์ธ์ค ํ ํฐ์ ์ฌ์ ํ ์ ํจํฉ๋๋ค. ํ์ํ ๊ฒฝ์ฐ axios ํค๋๋ฅผ ์
๋ฐ์ดํธํ์ธ์.
basicAxios.defaults.headers.common[
"Authorization"
] = `Bearer ${accessToken}`;
}
} else {
// ์ก์ธ์ค ํ ํฐ์ ์ฐพ์ ์ ์์ต๋๋ค. ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์์๋ ์ ์์ต๋๋ค.
logout();
}
}, [refreshAccessToken, logout]);
useEffect(() => {
checkAndRefreshToken();
}, [checkAndRefreshToken]);
return { user, isLoggedIn, checkAndRefreshToken };
};
export default useAuth;
ํ์ฌ ๋ก์ง์ ๋ฆฌํ๋ ์ ํ ํฐ 15๋ถ์์ 5๋ถ ์ง๋ฆฌ ์ก์ธ์ค ํ ํฐ 3๋ฒ ์ฌ๋ฐ๊ธ ๋ฐ๊ณ 4๋ฒ ์งธ ์ก์ธ์ค ํ ํฐ ์ฌ๋ฐ๊ธ api ์์ฒญํ ๋ ๋ฆฌํ๋ ์ ํ ํฐ์ด ์๊ธฐ ๋๋ฌธ์ ๋ก์ปฌ์์ ์ก์ธ์ค ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด์ ๋ก๊ทธ์์์ด ๋๊ณ ์์ต๋๋ค.
์ ์ผ ์์ ํ ๋ฐฉ๋ฒ
: Refresh Token์ http only, secure Cookie์ ์ ์ฅ & Access Token์ ๋ก์ปฌ ๋ณ์์ ์ ์ฅ
=> CSRF ๊ณต๊ฒฉ : Cookie์ Access Token์ด ์๊ธฐ์ ์ธ์ฆ ๋ถ๊ฐ ์ํ. http only secure ์ฟ ํค ํน์ฑ์ refresh token ์์ฒด๋ฅผ ํธ ๋ฐฉ๋ฒ์ด ์์
=> XSS ๊ณต๊ฒฉ : Access Token์ ๋ก์ปฌ ๋ณ์์ ์ ์ฅ๋์ด ์๊ธฐ์ ํ์ทจ ๋ถ๊ฐ
=> Access Token์ ๋ง๋ฃ์๊ฐ์ ์งง๊ฒ ๊ฐ์ ธ๊ฐ์ผ๋ก์จ ๊ทธ๋๋ง ์ทจ์ฝํ XSS๋ฅผ ์ข ๋ ๋ฐฉ์ดํ ์ ์์
์ถ๊ฐ์ ์ผ๋ก ํ๋ก์ ํธ ์งํ ๊ฐ์ ๋ฐฑ์๋์ ํ๋ก ํธ ์ฌ์ด์ ์ฟ ํค ์ ์ก ๋ฌธ์ ๋ค์ ๋ค๋ค๋ณผ๋ ค๊ณ ํ๋ค.
SameSite ๊ธฐ๋ณธ ์์ฑ ๊ฐ์ Lax์ด๋ค. ๊ทธ๋์ ๋์ผ ์ฌ์ดํธ์์๋ง ์ฟ ํค๋ฅผ ์ ์กํ๋๋ก ์ ํํ์ฌ CSRF ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ๋๋ฌธ์ ํ๋ก ํธ๋ก ์ฟ ํค ์ ์ก ๋ถ๊ฐ๋ฅํ ์ํฉ์ด ๋ฐ์ํ์๋ค. ๊ทธ ์ด์ ๋ ํ๋ก ํธ ์๋ฒ์ API ๋ฐฑ์๋ ์๋ฒ์ ๋๋ฉ์ธ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค.
ํ์ง๋ง SameSite=None
์ผ ๊ฒฝ์ฐ Secure
์์ฑ์ true
๋ก ์ง์ ํด์ผ ํ๋ค.
์ด ์ค์ ์ ํด๋น ์ฟ ํค๊ฐ HTTPS ํ๋กํ ์ฝ์ ํตํด์๋ง ์ ์ก๋๋๋ก ํ๋ค. ๋ฐ๋ผ์ HTTP๋ฅผ ์ฌ์ฉํ๋ ์ฐ๊ฒฐ์์๋ ์ด ์ฟ ํค๊ฐ ์ ์ก๋์ง ์๋๋ค.
SameSite=None, Secure;
์ค์ ์ ์ํด ๊ฐ๋น์์์ ๋๋ฉ์ธ์ ๊ตฌ๋งคํ๊ณ SSL ์ธ์ฆ์๋ฅผ ๋ฐ๊ธํ์ฌ HTTPS ํ๋กํ ์ฝ์ ์ ์ฉํ์๋ค. ํ์ง๋ง ๊ทธ๋ผ์๋ API๋ฅผ ํตํด ์ฟ ํค๋ฅผ ๋ฐ์์ค๋ฉด ๊ฑฐ๋ถ๋๊ณ ์์๋ค.
"refresh_token" ์ฟ ํค๋ ์ธ๋ถ ์ฟ ํค์ด๊ณ "Partitioned" ์์ฑ์ด ์๊ธฐ ๋๋ฌธ์ ๊ณง ๊ฑฐ๋ถ๋ฉ๋๋ค.
SameSite=None; Secure
ย ์ค์ ์ย ๊ฐ์งย ์ฟ ํค๋ค์ดย ํฌ๋ก์คย ์ฌ์ดํธ(๋ค๋ฅธย ๋๋ฉ์ธย ๊ฐ)ย ์ปจํ
์คํธ์์ย ์๋ํ๋ย ๊ฒฝ์ฐ,ย ์ด๋ค์ย ์จ๋ํํฐย ์ฟ ํค๋กย ์ทจ๊ธํ๊ณ ,ย ํฌ๋กฌ์์ย ์ด๋ฌํย ์ฟ ํค์ย ์ฌ์ฉ์ย ์ฐจ๋จํ๋ค๊ณ ํ๋ค. ์ด๋ย ์ฌ์ฉ์์ย ๊ฐ์ธ์ ๋ณดย ๋ณดํธ๋ฅผย ๊ฐํํ๊ณ ,ย ํฌ๋ก์คย ์ฌ์ดํธย ์ถ์ ์ย ๋ฐฉ์งํ๊ธฐย ์ํย ์กฐ์น๋ผ๊ณ ํ๋ค.
ํ๋ก ํธ ์๋ ์๋ฒ์ ์ฃผ์๋ www.fordogs.store
์ด๊ณ ๋ฐฑ์๋ API ์๋ฒ์ ์ฃผ์๋ api.fordogs.store
์ด๋ค. ๊ฐ์ ๋๋ฉ์ธ์์๋ ์ธ๋ถ ์ฟ ํค๋ก ์ทจ๊ธ๋๋ ์ด์ ๋ฅผ ํ์ธํด๋ณด๋ domain
์์ฑ ๋ฌธ์ ์๋ค.
domain
ย ์์ฑ์ ์๋ฌด ๊ฐ๋ ๋ฃ์ง ์์๋ค๋ฉด, ์ฟ ํค๋ฅผ ์ค์ ํ ๋๋ฉ์ธ์์๋ง ์ฟ ํค์ ์ ๊ทผํ ์ ์๋ค. ์๋ธ ๋๋ฉ์ธ์์๋ ์ฟ ํค์ ์ ๊ทผํ ์ ์๋ค. ๋๋ฌธ์ domain
ย ์์ฑ์ ๋ฃจํธ ๋๋ฉ์ธ์ธย domain=fordogs.store
๋ฅผ ๋ช
์์ ์ผ๋ก ์ค์ ํ์ฌ ์ ๊ทผํ ์ ์๋๋ก ํ์๋ค.
์ถ๊ฐ๋ก ๊ฐ์ ๋๋ฉ์ธ์ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ตณ์ด SameSite=None
๋ฅผ ํ ํ์๊ฐ ์์๊ธฐ ๋๋ฌธ์ SameSite=Strict
๋ก ์์ฑ์ ๋ณ๊ฒฝํ์๋ค.
JWT ๋ฆฌํ๋ ์ ํ ํฐ์ XSS ๊ณต๊ฒฉ๊ณผ js ์ ๊ทผ์ ์ฐจ๋จํ์ฌ ๋ณด์์ฑ์ ์ฌ๋ฆฌ๊ธฐ ์ํด httpOnly ์ฟ ํค
์ ๋ฃ์ด์ ์ ๋ฌ์ ๋ฐ์๋๋ฐ, same-site ๊ธฐ๋ณธ๊ฐ์ด Lax๋ก ์ค์ ๋์ด์๊ธฐ ๋๋ฌธ์ ๋์ผ ๋๋ฉ์ธ์ด ์๋ ๋ก์ปฌ ๊ฐ๋ฐํ๊ฒฝ๊ณผ ์๋ฒ ๊ฐ์ ์ฟ ํค ์ ๋ฌ์ด ์ด๋ฃจ์ด์ง์ง ์์์ต๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด same-site๋ฅผ none์ผ๋ก ์ค์ ํ๊ธฐ๋ฅผ ์์ฒญํ์๊ณ ์ด ๊ณผ์ ์์ ๋ฐฑ์๋ ์ธก์ secure : true ์์ฑ์ ์ถ๊ฐํด์ค์ผํ๋๋ฐ secure : ture๋ https ํ๋กํ ์ฝ์์๋ง ์ ์ฉ์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ค์ ํ๊ธฐ ์ํด์๋ SSL ์ธ์ฆ์๊ฐ ํ์ํ์ต๋๋ค. ๊ทธ๋ฐ๋ฐ SSL ์ธ์ฆ์๋ ํน์ ๋๋ฉ์ธ์์๋ง ๋ฐ๊ธ ๋ฐ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ฉ์ธ์ ๊ตฌ์ ํ์ฌ ์๋ฒ์์ SSL ์ธ์ฆ์๋ฅผ ๋ฐ๊ธ ๋ฐ์ ํ https ๋ฅผ ์ ์ฉํ์๊ณ ํ๋ก ํธ ๋ก์ปฌ ํ๊ฒฝ์์๋ https ์ ์ก์์ ํ๊ธฐ ์ํด https ์ค์ ์ ํด์ฃผ์์ต๋๋ค.
์ดํ ๋ธ๋ผ์ฐ์ ์์ Partitioned ์ฟ ํค๋ก ๋ถ๋ฅ๋๋ ๋ฌธ์ ๊ฐ ์์๋๋ฐ, ์๋ฒ์ ํ๋ก ํธ์ ๋๋ฉ์ธ์ ๋์ผํ๊ฒ ๋ง์ถ๊ณ SameSite=Strict๋ก ๋ณ๊ฒฝํด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์์ต๋๋ค.