11. ๐ ํ๋ก ํธ์๋ ๋ณด์ ์ํ๊ณผ ๋ฐฉ์ด ์ ๋ต
๐ ๊ฐ์
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ๋ฐ๋์ ์์์ผ ํ XSS, CSRF ๊ณต๊ฒฉ ๋์๋ฒ๊ณผ ์์ ํ ์ธ์ฆ ์์คํ ๊ตฌ์ถ ์ ๋ต์ ๋ง์คํฐํฉ๋๋ค.
๐ ์ด ๋ฉด์ ํญ๋ชฉ์ ๋ชฉํ
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 20๋ถ (ํต์ฌ ์์ฝ: 10๋ถ)
๐บ๏ธ ์ด ์ฑํฐ์ ํ๋ฆ
[๊ฐ๋
์ฌ์ ] โ [์ง๋ฌธ 1: XSS & CSRF ๋ฐฉ์ด] โ [์ง๋ฌธ 2: ํ ํฐ ๋ณด์ & SSR ์ธ์ฆ] โ [์ค์ ๋ณํ ์ง๋ฌธ]
๐ฏ ์ด ์ฑํฐ๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ๋ฐ์ํ ์ ์๋ ์ฃผ์ ๋ณด์ ์ํ์ ์๋ฆฌ์ ๋ฐฉ์ด ๊ธฐ๋ฒ์ ์ค๋ช ํฉ๋๋ค.
Content Security Policy(CSP)๋ฅผ ํ์ฉํ์ฌ ๋ณด์ ์์ค์ ๊ฐํํ๋ ๋ฒ์ ์ตํ๋๋ค.- ์ Access Token์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๋ฉด ์ ๋๋์ง ๋ณด์์ ๊ด์ ์์ ๋ ผํฉ๋๋ค.
๐ ํต์ฌ ๊ฐ๋ ์ฌ์ (Concept Glossary)
1. XSS (Cross-Site Scripting)
๊ณต๊ฒฉ์๊ฐ ์ ์ฑ ์คํฌ๋ฆฝํธ๋ ์ํํ HTML์ ์ฝ์ ํ์ฌ ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๊ฒ ๋ง๋๋ ๊ณต๊ฒฉ์ ๋๋ค. ์ฟ ํค ํ์ทจ, ์ธ์ ๊ฐ๋ก์ฑ๊ธฐ, ์ฌ์ฉ์ ๋์ ์์ฒญ ๋ณด๋ด๊ธฐ ๋ฑ์ด ๊ฐ๋ฅํด์ง๋ฏ๋ก ์ ๋ ฅ๊ณผ ์ถ๋ ฅ ๊ฒฝ๊ณ๋ฅผ ๋ชจ๋ ๊ด๋ฆฌํด์ผ ํฉ๋๋ค.
2. CSRF (Cross-Site Request Forgery)
์ฌ์ฉ์๊ฐ ์์ ์ ์์ง์ ๋ฌด๊ดํ๊ฒ ๊ณต๊ฒฉ์๊ฐ ์๋ํ ํ์(๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ, ์ก๊ธ ๋ฑ)๋ฅผ ํน์ ์น์ฌ์ดํธ์ ์์ฒญํ๊ฒ ๋ง๋๋ ๊ณต๊ฒฉ์ ๋๋ค. ์ฌ์ฉ์๊ฐ ์ด๋ฏธ ์ธ์ฆ๋ ์ธ์ ์ ๊ฐ์ง๊ณ ์๋ค๋ ์ ์ ์ ์ฉํฉ๋๋ค.
3. HttpOnly & Secure Cookie
- HttpOnly: ์๋ฐ์คํฌ๋ฆฝํธ(
document.cookie)๋ก ์ฟ ํค์ ์ ๊ทผํ๋ ๊ฒ์ ๋ง์ XSS๋ก๋ถํฐ ํ ํฐ์ ๋ณดํธํฉ๋๋ค. - Secure: ์ค์ง HTTPS ์ฐ๊ฒฐ์์๋ง ์ฟ ํค๊ฐ ์ ์ก๋๋๋ก ํ์ฌ ํต์ ๊ณผ์ ์ ์ ์ถ์ ๋ฐฉ์งํฉ๋๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ๐ฃ ์์ฒ (ํ๋ฐ): "์ํธ ๋! '์์๋ค ๊ฒ์ํ' ๋๊ธ์ ๋๊ตฐ๊ฐ ์ํํ HTML์ ๋ฃ์์ต๋๋ค. ์ง๊ธ์ ๋จ์ ์๋ฆผ์ฒ๋ผ ๋ณด์์ง๋ง, ๋ก๊ทธ์ธ ์ ๋ณด๋ ์ฌ์ฉ์ ํ๋์๋ ์ํฅ์ ์ค ์ ์๊ฒ ์ฃ ?"
- ๐ฆ ์ํธ (๋ฆฌ๋): "๋ง์์. ๋ณด์์ ๊ธฐ๋ฅ์ด ๋๋ ๋ค ๋ถ์ด๋ ์ฅ์์ด ์๋๋ผ ์ ๋ ฅ, ์ ์ฅ, ์ถ๋ ฅ, ์ธ์ฆ ํ๋ฆ ์ ์ฒด์ ์กฐ๊ฑด์ ๋๋ค. XSS์ CSRF๋ ๊ณต๊ฒฉ ๊ฒฝ๋ก๊ฐ ๋ค๋ฅด๋ ๋ฐฉ์ด๋ ๋ค๋ฅด๊ฒ ์ค๊ณํด์ผ ํด์."
๋ฉด์ ์ง๋ฌธ 1. XSS์ CSRF ๊ณต๊ฒฉ์ ๊ฒฐ์ ์ ์ธ ์ฐจ์ด์ ๊ณผ, ๊ฐ๊ฐ์ ํ๋ก ํธ์๋ ์ธก ๋ฐฉ์ด ์ ๋ต์ ์ค๋ช ํด ๋ณด์ธ์.
๐ฏ ์ถ์ ์๋
์น ๋ณด์์ 2๋ ์ฐ๋งฅ์ ์ ํํ ๊ตฌ๋ถํ๊ณ ์๋์ง ํ์ธํฉ๋๋ค. ๊ณต๊ฒฉ์ '์ฃผ์ฒด'์ '๋์'์ ์ดํดํด์ผ ์ฌ๋ฐ๋ฅธ ๋ฐฉ์ด ๋ก์ง์ ์งค ์ ์์ต๋๋ค.
๐ฃ ์์ฒ ์ด์ Naive ๊ตฌํ (Bad Case)
์์ฒ ์ด๋ ๊ฒ์ํ ๋ฐ์ดํฐ๋ฅผ dangerouslySetInnerHTML๋ก ์๋ฌด ๊ฒ์ฆ ์์ด ๋ ๋๋งํ๊ณ ์์ต๋๋ค.
// ๐ฃ ์์ฒ : "์๋ฒ์์ ์จ ๋ฐ์ดํฐ๋๊น ๋ฏฟ์ด๋ ๋๊ฒ ์ฃ ?"
function PostContent({ content }) {
// โ ๏ธ ์ํ: onerror, javascript: URL ๊ฐ์ HTML ์์ฑ์ด ์คํ๋ ์ ์์
// ์: '<img src=x onerror="fetch(`/steal?c=${document.cookie}`)">'
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}๐ฆ ์ํธ์ ๋ฆฌ๋ทฐ ํฌ์ธํธ
"์์ฒ ๋,dangerouslySetInnerHTML์ HTML์ React์ ๊ธฐ๋ณธ ์ด์ค์ผ์ดํ ๋ณดํธ ๋ฐ์์ ๋ฃ๊ฒ ๋ค๋ ์ ์ธ์ ๋๋ค. ์ฌ์ฉ์์ ์ ๋ ฅ์ ์ ๋ขฐํ์ง ๋ง๊ณ , CSRF๋ ๋ค๋ฅธ ์ถ์ฒ์ ํ์ด์ง๊ฐ ์ธ์ฆ๋ ์ฌ์ฉ์์ ๊ถํ์ ๋น๋ ค ์์ฒญ์ ๋ณด๋ด๋ ๋ฌธ์ ๋ผ๊ณ ๊ตฌ๋ถํ์ธ์."
๐ฆ ์ํธ์ ์ํคํ ์ฒ ๊ฐ์ด๋ (Good Case)
์ํธ ๋ฆฌ๋๊ฐ ์ ์ํ๋ 2์ค, 3์ค ๋ณด์ ๋ ์ด์ด์ ๋๋ค.
// ๐ฆ ์ํธ: "ํ ๊ฒน์ด ์คํจํด๋ ๋ค์ ๊ฒน์ด ๋ง๋๋ก ๋ฐฉ์ด์ธต์ ๋๋์ธ์."
// 1. XSS ๋ฐฉ์ด (Sanitization)
import DOMPurify from 'dompurify'; // โ
ํ์ฉ๋ ํ๊ทธ๋ง ๊ณจ๋ผ๋ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ฉ
function SafePostContent({ content }) {
const cleanContent = DOMPurify.sanitize(content);
return <div dangerouslySetInnerHTML={{ __html: cleanContent }} />;
}
// 2. CSRF ๋ฐฉ์ด
// - SameSite Cookie ์ค์ : cross-site ์์ฒญ์ ์ฟ ํค๊ฐ ์๋ ํฌํจ๋๋ ๋ฒ์๋ฅผ ์ ํ
// - CSRF Token: ์๋ฒ์์ ๋ฐ๊ธํ ์ผํ์ฉ ํ ํฐ์ ํค๋์ ๋ด์ ์ ์ก
// - Custom Header: ๋จ์ ์์ฒญ์ ํผํ๊ณ ์๋ฒ์ Origin/ํ ํฐ ๊ฒ์ฆ๊ณผ ํจ๊ป ์ฌ์ฉReact๋ ์ผ๋ฐ ํ ์คํธ ๋ ๋๋ง์์ ๊ฐ์ ์ด์ค์ผ์ดํํ์ง๋ง, rich text ์๊ตฌ ๋๋ฌธ์ HTML์ ์ง์ ๋ฃ๋ ์๊ฐ ๋ฐฉ์ด ์ฑ ์์ด ๊ฐ๋ฐ์์๊ฒ ์ต๋๋ค. sanitizer, CSP, ํ์ฉ ํ๊ทธ ์ ์ฑ ์ ํจ๊ป ์ ํด์ผ "๋ณด์ด๋ ๊ธฐ๋ฅ"๊ณผ "์์ ํ ์ถ๋ ฅ"์ ๋์์ ๋ง์กฑํ ์ ์์ต๋๋ค.
๐ ๋ ๋ฒจ๋ณ ๋ต๋ณ ๊ฐ์ด๋ (Self-Check)
- Level 1 (Junior): "XSS๋ ๊ฒ์ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ๋ฆฌ๋ ๊ฒ์ด๊ณ , CSRF๋ ๋จ์ ์ฌ์ดํธ์์ ๋ด ๊ณ์ ์ผ๋ก ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์ ๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์จ์ ๋ง์์ผ ํฉ๋๋ค."
- Level 2 (Senior): "XSS ๋ฐฉ์ด๋ฅผ ์ํ ์ด์ค์ผ์ดํ(Escape) ์ฒ๋ฆฌ์ DOMPurify ํ์ฉ, CSP ์ค์ ์ ์ค์์ฑ์ ์ค๋ช
ํฉ๋๋ค. CSRF ๋ฐฉ์ด๋ฅผ ์ํด์๋ ์ฟ ํค์
SameSite์์ฑ(Strict/Lax)์ด ์ด๋ป๊ฒ ์์ฒญ์ ์ ์ดํ๋์ง ์๋ฆฌ๋ฅผ ์ ์ํฉ๋๋ค." - Level 3 (Specialist): "ํ๋์ ์ธ SPA ํ๊ฒฝ์์ JWT๋ฅผ ํ์ฉํ ๋์ ๋ณด์ ํธ๋ ์ด๋์คํ๋ฅผ ๋
ผํฉ๋๋ค. XSS๋ '์คํฌ๋ฆฝํธ ์คํ ์ ์ง'๊ฐ ํต์ฌ์ด๊ณ , CSRF๋ '์์ฒญ์ ์ถ์ฒ ๊ฒ์ฆ'์ด ํต์ฌ์์ ์ํคํ
์ฒ ๊ด์ ์์ ๋ถ์ํฉ๋๋ค. ๋ํ
Referrer-Policy๋STS(Strict-Transport-Security)๊ฐ์ ๊ณ ๊ธ ๋ณด์ ํค๋ ํ์ฉ ๊ฒฝํ์ ๊ณต์ ํฉ๋๋ค."
๋ฉด์ ์ง๋ฌธ 2. ์ธ์ฆ ํ ํฐ(Access Token)์ ์ ์ฅํ ๋ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ฟ ํค ์ค ์ด๋ ๊ฒ์ด ๋ ์์ ํ๊ฐ์? ๊ทธ ์ด์ ์ ์ค์ต์ ์ธ ๋ณด์ ์ ๋ต์ ์ค๋ช ํด ๋ณด์ธ์.
๐ฏ ์ถ์ ์๋
๋ณด์๊ณผ ํธ์์ฑ ์ฌ์ด์ ํธ๋ ์ด๋์คํ๋ฅผ ์ดํดํ๊ณ ์๋์ง ํ์ธํฉ๋๋ค. ์ ๋ต์ ์์ง๋ง, ๊ฐ ์ ํ์ง์ ๋ฐ๋ฅธ '์ํ ๋ชจ๋ธ๋ง' ๋ฅ๋ ฅ์ ํ๊ฐํฉ๋๋ค.
๐ฃ ์์ฒ ์ด์ Naive ๊ตฌํ (Bad Case)
์์ฒ ์ด๋ ๊ตฌํ์ด ํธํ๋ค๋ ์ด์ ๋ก Access Token์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅํฉ๋๋ค.
// ๐ฃ ์์ฒ : "์๋ก๊ณ ์นจํด๋ ๋ก๊ทธ์ธ ์ ํ๋ฆฌ๊ฒ ํ๋ ค๋ฉด ๋ก์ปฌ ์คํ ๋ฆฌ์ง๊ฐ ์ ์ผ ํธํด์!"
localStorage.setItem('accessToken', token);
// โ ๏ธ ์ฝ์ : XSS๊ฐ ๋ฐ์ํ๋ฉด ์คํ๋ ์คํฌ๋ฆฝํธ๊ฐ ํ ํฐ์ ์ฝ์ด ์ธ๋ถ๋ก ๋ณด๋ผ ์ ์์๐ฆ ์ํธ์ ๋ฆฌ๋ทฐ ํฌ์ธํธ
"์์ฒ ๋, ๋ก์ปฌ ์คํ ๋ฆฌ์ง๋ ๊ฐ์ origin์์ ์คํ๋๋ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์ฝ์ ์ ์์ต๋๋ค. XSS๊ฐ ์ด๋ฏธ ๋ฐ์ํ ์ํฉ์์๋ ๊ทธ ํธ๋ฆฌํจ์ด ํ ํฐ ์ ์ถ ๊ฒฝ๋ก๊ฐ ๋ ์ ์์ด์. ์ ์ฅ์ ์ ํ์ ์ด๋ค ๊ณต๊ฒฉ์๊ฐ ๋ฌด์์ ์ฝ๊ฑฐ๋ ๋ณด๋ผ ์ ์๋์ง๋ก ํ๋จํด์ผ ํฉ๋๋ค."
๐ฆ ์ํธ์ ์ํคํ ์ฒ ๊ฐ์ด๋ (Good Case)
์ํธ ๋ฆฌ๋๊ฐ ์ ์ํ๋ 'ํ์ด๋ธ๋ฆฌ๋ ๋ณด์ ์ ๋ต'์ ๋๋ค.
// ๐ฆ ์ํธ: "์ค์ํ ์ด์ ๋ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ๋ง์ง ์ ์๋ ๊ณณ์ ๋์ธ์."
/*
1. Refresh Token: HttpOnly & Secure ์ฟ ํค์ ์ ์ฅ
- ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ ๊ทผ ๋ถ๊ฐ (XSS ๋ฐฉ์ด)
- CSRF ๋ฐฉ์ด๋ฅผ ์ํด SameSite ์ค์ ํ์
2. Access Token:
- ๋ฐฉ๋ฒ A: ๋ฉ๋ชจ๋ฆฌ(React State)์ ์ ์ฅ (๊ฐ์ฅ ์์ ํ์ง๋ง ์๋ก๊ณ ์นจ ์ ๋ ์๊ฐ)
- ๋ฐฉ๋ฒ B: ์งง์ ์ ํจ๊ธฐ๊ฐ์ ๊ฐ์ง ํ ํฐ์ผ๋ก ๊ด๋ฆฌํ๊ณ ,
Silent Refresh ๋ฉ์ปค๋์ฆ์ ํตํด ์ฟ ํค์ Refresh Token์ผ๋ก ์ฌ๋ฐ๊ธ
*/๐ ๋ ๋ฒจ๋ณ ๋ต๋ณ ๊ฐ์ด๋ (Self-Check)
- Level 1 (Junior): "์ฟ ํค๊ฐ ๋ ์์ ํฉ๋๋ค. HttpOnly ์ค์ ์ ํ๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ๋ชป ์ฝ๊ธฐ ๋๋ฌธ์ ๋๋ค."
- Level 2 (Senior): "๋ก์ปฌ ์คํ ๋ฆฌ์ง๋ XSS์ ์ทจ์ฝํ๊ณ , ์ฟ ํค๋ CSRF์ ์ทจ์ฝํ๋ค๋ ์ ์ ๋น๊ต ์ค๋ช
ํฉ๋๋ค. ๋ฐ๋ผ์ ์ฟ ํค ์ฌ์ฉ ์
SameSite: Lax/Strict์ค์ ๊ณผCSRF Token๋ฐฉ์์ ๋ณํํด์ผ ํจ์ ๊ฐ์กฐํฉ๋๋ค." - Level 3 (Specialist): "'์ธ์ ํ์ด์ฌํน' ๋ฐฉ์ด๋ฅผ ์ํ ํฌ๊ด์ ์ธ ์ ๋ต์ ์ ์ํฉ๋๋ค. ํ ํฐ์ ์ ํด ์๊ฐ(Idle time) ๊ด๋ฆฌ, ์ค๋ณต ๋ก๊ทธ์ธ ๊ฐ์ง, ๊ทธ๋ฆฌ๊ณ ํ๋ก ํธ์๋ BFF(Backend For Frontend) ํจํด์ ํตํด ํด๋ผ์ด์ธํธ์ ํ ํฐ์ ์์ ๋ ธ์ถํ์ง ์๋ ๋ณด์ ์ํคํ ์ฒ๋ฅผ ์ค๋ช ํฉ๋๋ค."
๐ ์ค์ ๋ณํ ์ง๋ฌธ (Related Variations)
๋ฉด์ ์ง๋ฌธ 212. CSP(Content Security Policy)๋ ๋ฌด์์ด๋ฉฐ, ์ ์ค์ ํด์ผ ํ๋์?
- ๐ฏ ์ถ์ ์๋: ๋ธ๋ผ์ฐ์ ๋ ๋ฒจ์์ ๋ณด์ ์ ์ฑ ์ ๊ฐ์ ํ๋ '์ตํ์ ๋ฐฉ์ด์ '์ ๋ํด ์๋์ง ํ์ธํฉ๋๋ค.
- ๐ก ํต์ฌ ์๋ฆฌ & ๋ต๋ณ: CSP๋ HTTP ํค๋๋ฅผ ํตํด ๋ธ๋ผ์ฐ์ ๊ฐ ์์์ ๋ก๋ํ ์ ์๋ ์์ ํ ์ถ์ฒ(Origin)๋ฅผ ๋ช ์ํ๋ ๋ณด์ ๋ ์ด์ด์ ๋๋ค. ์๋ฅผ ๋ค์ด "์คํฌ๋ฆฝํธ๋ ์ค์ง ์ฐ๋ฆฌ ์๋ฒ์ Google Analytics์์๋ง ๋ฐ์์!"๋ผ๊ณ ์ ์ธํ๋ฉด, ๊ณต๊ฒฉ์๊ฐ ์ ์ฑ ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์ ํด๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์คํ์ ๊ฑฐ๋ถํฉ๋๋ค. ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ์คํ ๊ธ์ง, Eval ์ฌ์ฉ ๊ธ์ง ๋ฑ์ ํตํด XSS ์ํ์ ํ๊ธฐ์ ์ผ๋ก ๋ฎ์ถ ์ ์์ต๋๋ค.
๋ฉด์ ์ง๋ฌธ 225. HTTPS์ ๋์ ์๋ฆฌ์ ์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ ์ด๋ฅผ ์ ํํ ์์์ผ ํ๋์?
- ๐ฏ ์ถ์ ์๋: ๋ฐ์ดํฐ ์ํธํ์ ์ธ์ฆ์ ์ฒด๊ณ์ ๋ํ ๊ธฐ๋ณธ ์์์ ํ์ธํฉ๋๋ค.
- ๐ก ํต์ฌ ์๋ฆฌ & ๋ต๋ณ: HTTPS๋ ๊ณต๊ฐํค์ ๋์นญํค ์ํธํ ๋ฐฉ์์ ํผํฉํ์ฌ ํด๋ผ์ด์ธํธ์ ์๋ฒ ์ฌ์ด์ ํต์ ์ ๋ณดํธํฉ๋๋ค. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ ๋จ์ํ '์ฃผ์์ฐฝ์ ์๋ฌผ์ '๋ง ๋ณด๋ ๊ฒ์ด ์๋๋ผ,
Secure์ฟ ํค ์ค์ ์ด HTTPS ํ๊ฒฝ์์๋ง ์๋ํ๋ค๋ ์ , ๊ทธ๋ฆฌ๊ณ ์ค๊ฐ์ ๊ณต๊ฒฉ(MITM) ์ ๋ณด์ ๊ฒฝ๊ณ ๊ฐ ๋ฐ์ํ๋ ๋ฉ์ปค๋์ฆ์ ์ดํดํ์ฌ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ ์ ์ก ์ ์์ ์ฑ์ ๋ณด์ฅํด์ผ ํฉ๋๋ค.
๋ฉด์ ์ง๋ฌธ 230. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ ๋ณด์(Supply Chain Attack)์ ๋ฐฉ์ดํ๋ ๋ฐฉ๋ฒ์?
- ๐ฏ ์ถ์ ์๋: ๋ด๊ฐ ์ง ์ฝ๋๋ฟ๋ง ์๋๋ผ ๋จ์ด ๋ง๋ ์ฝ๋(
node_modules)์ ์ํ์ฑ์ ๊ด๋ฆฌํ๋์ง ํ์ธํฉ๋๋ค. - ๐ก ํต์ฌ ์๋ฆฌ & ๋ต๋ณ:
npm audit์ ์ฃผ๊ธฐ์ ์ผ๋ก ์คํํ์ฌ ์๋ ค์ง ์ทจ์ฝ์ ์ ์ฒดํฌํ๊ณ ,lockํ์ผ์ ํตํด ๋ชจ๋ ํ์์ด ๋์ผํ๊ณ ์์ ํ ๋ฒ์ ์ ํจํค์ง๋ฅผ ์ฌ์ฉํ๋๋ก ์ฌ์ฉํด์ผ ํฉ๋๋ค. ๋ํ ์ค์ ํ๋ก์ ํธ๋ผ๋ฉดSnyk๊ฐ์ ๋ณด์ ๋๊ตฌ๋ฅผ CI/CD์ ์ฐ๋ํ์ฌ ์ทจ์ฝ์ ์ด ํฌํจ๋ ์ฝ๋์ ๋ฐฐํฌ๋ฅผ ์๋ ์ฐจ๋จํ๋ ํ๊ฒฝ์ ๊ตฌ์ถํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. XSS์ CSRF์ ์ฐจ์ด๋ฅผ ๋ฉด์ ์์ ์ค๋ช ํ ๋ ๊ฐ์ฅ ์ค์ํ ๊ตฌ๋ถ์ ๋ฌด์์ธ๊ฐ์?
โ
์ ๋ต: XSS๋ ๊ณต๊ฒฉ ์คํฌ๋ฆฝํธ๊ฐ ์ฌ์ฉ์ ๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ ๋ฌธ์ ์ด๊ณ , CSRF๋ ์ธ์ฆ๋ ์ฌ์ฉ์์ ๊ถํ์ผ๋ก ์์น ์๋ ์์ฒญ์ด ๋ณด๋ด์ง๋ ๋ฌธ์ ๋ผ๋ ์
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช : XSS๋ ์ ๋ ฅ ๊ฒ์ฆ, ์ถ๋ ฅ ์ธ์ฝ๋ฉ, sanitizer, CSP๋ก ๋ง๊ณ , CSRF๋ SameSite ์ฟ ํค, CSRF token, custom header ๊ฐ์ ์์ฒญ ๊ฒ์ฆ์ผ๋ก ๋ง์ต๋๋ค. ๊ณต๊ฒฉ์ ๊ฒฝ๋ก๊ฐ ๋ค๋ฅด๋ ๋ฐฉ์ด๋ ๋ฌ๋ผ์ผ ํฉ๋๋ค.
- ์ค๋ต ํผ๋๋ฐฑ: ๋ ๋ค "ํดํน"์ด๋ผ๊ณ ๋ง ๋งํ๋ฉด ์
HttpOnly๊ฐ XSS์ ๋์ ๋๊ณ , ์ SameSite๊ฐ CSRF์ ์ค์ํ์ง ์ค๋ช ํ ์ ์์ต๋๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋๋ ๊ณต๊ฒฉ์ธ์ง, ์์ฒญ์ด ์์กฐ๋๋ ๊ณต๊ฒฉ์ธ์ง ๋จผ์ ๋๋๋๋ค.
Q2. Access Token์ localStorage์ ์ค๋ ๋ณด๊ดํ๋ ๋ฐฉ์์ด ์ํํ ์ด์ ๋ ๋ฌด์์ธ๊ฐ์?
โ
์ ๋ต: XSS๊ฐ ๋ฐ์ํ๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ๋ก ํ ํฐ์ ์ฝ์ด ์ธ๋ถ๋ก ์ ์ถํ ์ ์๊ธฐ ๋๋ฌธ
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช : ๋ณด์ ์ค๊ณ๋ ํธ์์ฑ๊ณผ ์ํ์ ๊ท ํ์ ๋๋ค. Refresh Token์ HttpOnly/Secure/SameSite ์ฟ ํค๋ก ๋ณดํธํ๊ณ , Access Token์ ์งง์ ์๋ช ๊ณผ ๋ฉ๋ชจ๋ฆฌ ์ ์ฅ/BFF ํจํด ๋ฑ์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค.
- ์ค๋ต ํผ๋๋ฐฑ: ์ฟ ํค๊ฐ ํญ์ ์์ ํ๋ค๋ ๋ป๋ ์๋๋๋ค. ์ฟ ํค๋ CSRF ๋ฐฉ์ด ์ค์ ์ด ํจ๊ป ํ์ํฉ๋๋ค.
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ํ ํฐ ์ ์ฅ์๋ฅผ ๊ณ ๋ฅผ ๋๋ ์ด๋ค ๊ณต๊ฒฉ์๊ฐ ๋ฌด์์ ์ฝ์ ์ ์๋์ง๋ถํฐ ๋ด ๋๋ค.
Q3. ์์ฒ ์ด์ ํ
์คํธ ํ์: dangerouslySetInnerHTML์ ์จ์ผ๋ง ํ๋ ์๊ตฌ์ฌํญ์ด ์์ ๋ ์ต์ํ ํ์ธํด์ผ ํ ๊ฒ์ ๋ฌด์์ธ๊ฐ์?
โ
์ ๋ต: ์ ๋ขฐํ ์ ์๋ HTML์ sanitizer๋ก ์ ์ ํ๊ณ , CSP์ ํ์ฉ ํ๊ทธ ์ ์ฑ
์ ํจ๊ป ๊ฒํ ํ๋ค
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช : rich text๋ฅผ ๋ ๋๋งํด์ผ ํ๋ ์๊ตฌ๋ ํ์ค์ ์ผ๋ก ์กด์ฌํฉ๋๋ค. ์ค์ํ ๊ฑด "์๋ฒ์์ ์์ผ๋ ๋ฏฟ๋๋ค"๊ฐ ์๋๋ผ, ์ ๋ ฅ/์ ์ฅ/์ถ๋ ฅ ์ด๋ ๋จ๊ณ์์ ์ด๋ค HTML์ ํ์ฉํ ์ง ๋ช ํํ ํ๋ ๊ฒ์ ๋๋ค.
- ์ค๋ต ํผ๋๋ฐฑ:
dangerouslySetInnerHTML์ ๊ธ์งํ๋ค๊ณ ๋ง ๋ตํ๋ฉด ์ค๋ฌด ์๊ตฌ๋ฅผ ํด๊ฒฐํ์ง ๋ชปํฉ๋๋ค. ์์ ํ ์ฌ์ฉ ์กฐ๊ฑด์ ๋งํด์ผ ํฉ๋๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ์ํํ API๋ ๊ธ์ง๊ฐ ์๋๋ผ ํต์ ์กฐ๊ฑด์ด ํต์ฌ์ ๋๋ค.
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ๋ณด์์ "๋์ค์ ๋ถ์ด๋ ์ต์ "์ฒ๋ผ ์๊ฐํ๋ ํ๋๋ฅผ ๋ฒ๋ ธ๋ค. ์์๋ค ๊ฒ์ํ ๋๊ธ ํ๋๊ฐ ํ ํฐ ํ์ทจ๋ก ์ด์ด์ง ์ ์๋ค๋ ๊ฑธ ๋ณด๊ณ , ํ๋ก ํธ์๋๊ฐ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์งํค๋ ์ฒซ ๋ฒ์งธ ๋ฐฉ์ด์ ์ด๋ผ๋ ๋ง์ด ์ค๊ฐ๋ฌ๋ค.
๐ก "๋ณด์์ ๊ธฐ๋ฅ์ ๋ง๋ ์ฅ๋ฒฝ์ด ์๋๋ผ, ๊ธฐ๋ฅ์ด ์ฌ์ฉ์ ํธ์์ ์ค๋ ๋ฒํฐ๊ฒ ํ๋ ์กฐ๊ฑด์ด๋ค."
๋ค์ ๋ฆฌ๋ทฐ์์๋ ์ ์ฅ์์ ์ฟ ํค ์ค์ ๋ง ๋ณด๋ ๊ฒ ์๋๋ผ, ๊ณต๊ฒฉ์๊ฐ ์ด๋ค ๊ฒฝ๋ก๋ก ๋ค์ด์ฌ ์ ์๋์ง ๋จผ์ ๊ทธ๋ ค๋ด์ผ๊ฒ ๋ค. ์์ฒ ์ด ์ด์ ํธํ ๊ตฌํ๊ณผ ์์ ํ ๊ตฌํ ์ฌ์ด์ ๋น์ฉ์ ๋ฉด์ ์์ ์ค๋ช ํ ์ ์์ ๊ฒ ๊ฐ๋ค.