11. ๐Ÿ” ํ”„๋ก ํŠธ์—”๋“œ ๋ณด์•ˆ ์œ„ํ˜‘๊ณผ ๋ฐฉ์–ด ์ „๋žต

2026๋…„ 4์›” 30์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฐ˜๋“œ์‹œ ์•Œ์•„์•ผ ํ•  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)

์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์˜ ์˜์ง€์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ํ–‰์œ„(๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ, ์†ก๊ธˆ ๋“ฑ)๋ฅผ ํŠน์ • ์›น์‚ฌ์ดํŠธ์— ์š”์ฒญํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ณต๊ฒฉ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฏธ ์ธ์ฆ๋œ ์„ธ์…˜์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ์ ์„ ์•…์šฉํ•ฉ๋‹ˆ๋‹ค.

  • 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) ํŒจํ„ด์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์— ํ† ํฐ์„ ์•„์˜ˆ ๋…ธ์ถœํ•˜์ง€ ์•Š๋Š” ๋ณด์•ˆ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค."

๋ฉด์ ‘ ์งˆ๋ฌธ 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๋Š” ๊ธˆ์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ํ†ต์ œ ์กฐ๊ฑด์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

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

๐Ÿ’ก "๋ณด์•ˆ์€ ๊ธฐ๋Šฅ์„ ๋ง‰๋Š” ์žฅ๋ฒฝ์ด ์•„๋‹ˆ๋ผ, ๊ธฐ๋Šฅ์ด ์‚ฌ์šฉ์ž ํŽธ์—์„œ ์˜ค๋ž˜ ๋ฒ„ํ‹ฐ๊ฒŒ ํ•˜๋Š” ์กฐ๊ฑด์ด๋‹ค."

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