❄️5분만에 만든 UI 기능이 XSS 타임봄이 되다

발행: (2025년 12월 17일 오후 10:04 GMT+9)
8 min read
원문: Dev.to

Source: Dev.to

Source:

간단한 스크립트 — 사소한 시각 효과가 — 애플리케이션을 위험에 빠뜨릴 수 있을까?

아니요, 그렇습니다. 그리고 당신은 그 사실을 깨닫지 못할 수도 있습니다.

게다가 작고 무해해 보이는 코드 조각이 반짝이는 컬러풀한 시한폭탄이 될 수 있습니다. 어떻게 가능할까요? 가상의 이야기를 들어보세요.

❄️ 눈이 내리기 시작한다

당신이 웹사이트, 쇼핑몰, 혹은 웹 애플리케이션을 개발하고 있다고 상상해 보세요.

12월이 찾아옵니다. 불빛, 나무, 곳곳에 장식이 가득하고, 휴가 분위기가 당신에게 스며듭니다.

당신 — 또는 이해관계자 중 한 명이 — 작은 계절 감성을 원합니다.

“눈이 조금 내리면 어떨까요?” ❄️

당신은 흥분하고 바로 이 창의적인 작업에 뛰어듭니다.

하지만 잠깐.

백로그가 넘쳐나고, 마감일이 울부짖고, 갑자기 중요한 사실이 떠오릅니다:

당신은 게으르다. 😉

당연히 처음부터 코드를 작성하지는 않을 겁니다.

그래서 우리 모두가 하는 대로 합니다: CodePen, GitHub, 혹은 Stack Overflow를 열어 무작위 스니펫을 복사합니다.

❄️ 순수한 눈 스크립트

/**
 * ❄️ Simple snow effect
 * Source: random blog / CodePen
 */
const snowflakes = ["❄️", "❅", "❆"];

for (let i = 0; i 

5분 뒤 — 펑 — 아름다운 눈 내리는 효과가 완성됩니다.

Falling JavaScript snow and heading

당신은 이를 정식 코드 리뷰조차 거치지 않습니다.
혹은 누군가가 잠깐 훑어볼 뿐인데, “그냥 시각 효과잖아” 라고 생각합니다.

보안 테스트?
누가 눈 내리는 효과를 테스트하겠어요?

🎁 축하합니다 — 방금 XSS를 배포했습니다

문제는 바로 여기 있습니다:

el.innerHTML = snowflake;

XSS(교차 사이트 스크립팅)는 신뢰할 수 없는 데이터가 HTML이나 JavaScript로 실행될 수 있는 방식으로 DOM에 삽입될 때 발생합니다.

중요한 설명: 이 순간 이 코드는 아직 활성화된 XSS 취약점이 아닙니다. snowflake는 하드코딩된 완전 신뢰된 배열에서 나오기 때문이죠.

하지만 위험한 패턴은 이미 자리 잡고 있으며, 이것이 시한폭탄이 되는 이유입니다.

당장은 별다른 문제가 일어나지 않습니다. 모든 것이 정상적으로 보입니다.

⏳ 시간이 흐른다…

1월이 찾아옵니다. 경영진이 시각 효과를 끄자고 결정합니다.

“하지만 제거하지 마세요! 내년에도 필요할 거예요!”

혹은 더 일찍—봄이 오니 눈 대신 꽃잎이 흩날리게 🌸 하고 싶어합니다.

새로운 작업이 백로그에 추가됩니다. 다른 개발자가 이를 맡아 생각합니다:

“몇 달에 한 번씩 토글할 거면, 경영진이 직접 제어하게 하고—아이콘도 선택하게 하자.”

그래서 그들은 설정을 추가합니다. CMS를 통해서든, 원격 엔드포인트를 통해서든.

🌐 “작은 개선”

function fetchSeasonalConfig() {
  return Promise.resolve({
    enabled: true,
    snowflake: "❄️"
  });
}

fetchSeasonalConfig().then(config => {
  if (!config.enabled) return;

  for (let i = 0; i "

그게 전부입니다. 당신의 앱에 완전한 XSS가 들어갑니다.

🤔 “하지만 최신 프레임워크를 쓰고 있어요!”

몇몇은 이렇게 생각할지도 모릅니다:

“그런 얘기는 구식 jQuery 사이트에만 해당돼요. 저는 최신 프레임워크—React / Angular / Vue—를 쓰니까 XSS로부터 보호받을 거예요.”

그건 전혀 사실이 아닙니다.

⚠️ 프레임워크는 자신이 렌더링하는 부분만 보호한다

React와 Angular는 기본적으로 콘텐츠를 이스케이프하지만—오직 그들의 렌더링 시스템 안에서만.

다음과 같은 경우가 나오면:

  • innerHTML
  • dangerouslySetInnerHTML (React)
  • [innerHTML] 혹은 bypassSecurityTrustHtml (Angular)
  • 프레임워크 밖에서 실행되는 일반 JS 스크립트

…당신은 스스로 책임져야 합니다.

그리고 그 눈 스크립트는 종종 프레임워크 밖, index.html에 “작은 시각 효과”로 로드됩니다. 프레임워크는 무작위 JavaScript 파일을 샌드박스하지 않으니까요.

✅ 이렇게 하면 달라졌을 수도 있었던 이유

All of this could have been avoided with one simple change:

el.textContent = snowflake;

Or by:

  • HTML을 삽입하는 대신 DOM 노드를 명시적으로 생성하기
  • DOMPurify와 같은 잘 관리된 라이브러리로 HTML을 정화하기(엄격한 허용 목록을 사용해 적절히 구성)
  • 보안 경계를 명확히 정의하기: 외부 모든 것은 신뢰할 수 없음
  • “작은 UI 기능”도 핵심 기능과 동일한 보안 마인드셋으로 다루기

**Content Security Policy (CSP)**와 같은 방어‑깊이 전략도 영향을 줄일 수 있지만, 안전하지 않은 DOM API를 해결해 주지는 못합니다.

🎄 최종 생각

이 이야기가 정확히 일어났나요?
아니요 😉

비슷한 이야기를 수십 개 들어본 적이 있나요?
물론입니다.

기억하세요: 어떤 기능도 적절한 코드 리뷰와 보안 테스트를 건너뛰기에 너무 작지 않습니다.
디테일에 악마가 숨어 있습니다.

🎁 행복한 연휴

행복하고 평화로운 연휴가 되시길 바랍니다 — 인터넷에서 복사할 때 조금만 더 주의하면 스스로에게 줄 수 있는 그런 연휴 말이에요. ❄️✨

Back to Blog

관련 글

더 보기 »

창고 활용에 대한 종합 가이드

소개 창고는 근본적으로 3‑D 박스일 뿐입니다. Utilisation은 실제로 그 박스를 얼마나 사용하고 있는지를 측정하는 지표입니다. While logistics c...