Today's AntipatternAll Posts
ํ…Œ๋งˆ
GitHubToday's AntipatternAll Posts

์•ˆํ‹ฐํŒจํ„ด์„ ํ†ตํ•ด ๋” ๋‚˜์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›Œ๋ณด์„ธ์š”. ๊ฐœ๋ฐœ์ž๋“ค์ด ์‹ค์ˆ˜ํ•˜๋Š” ํŒจํ„ด๋“ค์„ ๋ถ„์„ํ•˜๊ณ  ๊ฐœ์„ ๋ฐฉ์•ˆ์„ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์—ฐ๊ฒฐํ•˜๊ธฐ

ยฉ 2025 Smelly.dev All rights reserved.

August 13, 2025

๐Ÿ™ˆ ๋ฌด๋ถ„๋ณ„ํ•œ `localStorage` ์‚ฌ์šฉ: ๊ฐœ์ธ ์ •๋ณด ์œ ์ถœ ๋ฐ XSS ๊ณต๊ฒฉ ์œ„ํ—˜ ์ฆ๊ฐ€

JavaScript
๋ณด์•ˆ
UX
์—๋Ÿฌ์ฒ˜๋ฆฌ
์›นํ‘œ์ค€
ํ˜ธํ™˜์„ฑ

Summary

localStorage์— ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ฑฐ๋‚˜ XSS ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉด ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ธก์—์„œ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ์ €์žฅํ•˜๊ณ , ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ๋ฐ ์ถœ๋ ฅ๊ฐ’ ์ธ์ฝ”๋”ฉ์„ ์ฒ ์ €ํžˆ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Why Wrong?

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

How to Fix?

localStorage์— ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์„ ์ตœ๋Œ€ํ•œ ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, ์„œ๋ฒ„ ์ธก์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ๋Š” ์ ‘๊ทผ ํ† ํฐ๋งŒ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ XSS ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•˜์ง€ ์•Š๋„๋ก ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ๋ฐ ์ถœ๋ ฅ๊ฐ’ ์ธ์ฝ”๋”ฉ์„ ์ฒ ์ €ํžˆ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. HTTPOnly ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ JavaScript์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋„๋ก ์„ค์ •ํ•˜๊ฑฐ๋‚˜, Web Crypto API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›น ์Šคํ† ๋ฆฌ์ง€ API๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” ํ•ญ์ƒ ๋ณด์•ˆ์ ์ธ ์ธก๋ฉด์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Before Code (Bad)

// โŒ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ localStorage์— ์ €์žฅ (์œ„ํ—˜)
localStorage.setItem('apiKey', 'YOUR_API_KEY');

// โŒ XSS ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•œ ๋ฐ์ดํ„ฐ๋ฅผ localStorage์— ์ €์žฅ (์œ„ํ—˜)
localStorage.setItem('username', '<script>alert("XSS");</script>');

// localStorage์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์™€ DOM์— ์ง์ ‘ ์‚ฝ์ž… (์œ„ํ—˜)
document.getElementById('username').innerHTML = localStorage.getItem('username');

After Code (Good)

// โœ… ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ์„œ๋ฒ„์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ํ† ํฐ๋งŒ ์ €์žฅ
localStorage.setItem('authToken', 'YOUR_AUTH_TOKEN');

// โœ… XSS ๊ณต๊ฒฉ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ๋ฐ ์ถœ๋ ฅ๊ฐ’ ์ธ์ฝ”๋”ฉ
function sanitizeInput(input) {
  const div = document.createElement('div');
  div.textContent = input;
  return div.innerHTML;
}

const username = localStorage.getItem('username');
if (username) {
  document.getElementById('username').innerHTML = sanitizeInput(username);
}

// โœ… Web Crypto API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•”ํ˜ธํ™”
async function encryptData(data) {
  const key = await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']
  );
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const encodedData = new TextEncoder().encode(data);
  const cipherText = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv: iv }, key, encodedData
  );
  return {
    cipherText: Array.from(new Uint8Array(cipherText)),
    iv: Array.from(iv),
    key: await crypto.subtle.exportKey('jwk', key)
  };
}

async function saveEncryptedData(data, keyName) {
  const encrypted = await encryptData(data);
  localStorage.setItem(keyName, JSON.stringify(encrypted));
}


// ์‚ฌ์šฉ ์˜ˆ์‹œ
saveEncryptedData('Sensitive Data', 'encryptedData');

You Might Also Like

localStorage์˜ ๋ณด์•ˆ ๋ฌธ์ œ ๋ฐ XSS ๊ณต๊ฒฉ์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋•์Šต๋‹ˆ๋‹ค.
https://owasp.org/www-community/attacks/xss/
Web Crypto API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API