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가지 중 골라서 사용하는 듯 하다.
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>
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>
npm install -D @types/sanitize-html
추가로, tsconfig.json
에 esModuleInterop=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 라이브러리
v-html
을 v-dompurify-html
로 바꾸기만 하면 되기 때문에, 이미 프로젝트가 많이 개발 된 상태에서 도입하여 빠르게 리팩토링 해야 하는 상황이라면 좋은 선택이 될 수 있다.sanitize-html
라이브러리에 비해 sanitizing 옵션 커스텀에 대한 내용이 npm
문서에 없는 걸 보면 별도의 커스텀은 지원하지 않는 것으로 보인다.sanitize-html 라이브러리
.vue
컴포넌트에서 import
후 사용할 수 있다.npm
문서를 보면 친절하게 설명이 잘 되어 있고, 허용할 태그와 차단할 태그 옵션을 설정해서 커스텀도 가능하다.Front-end
인 Vue
프레임워크에 대한 포스팅이기 때문에 본문에 언급은 안했지만, Node.js
기반 백엔드에서도 사용 가능한 라이브러리이다. 만약 백엔드도 Express
와 같이 Node.js
기반 프레임워크를 사용한다면, 통일성 측면에서 sanitize-html
라이브러리를 선택하는 것이 좋을 수도 있다.vue-dompurify-html
보다는 용량이 크고(66.1 kB) import
를 해야하기에 사용할 때 마다 코드 한두줄이 추가된다는 점.상황에 맞게 적절한 라이브러리를 선택해서 사용하면 된다.
지금 당장은 XSS 걱정이 없는 코드일지라도, 추후에 프로젝트가 커지고 기능이 확장되면서 계속 고도화가 되다보면
나도 모르는 부분에서 보안 이슈가 발생하고 제품의 결함으로 이어질 수 있다.
v-html
(innerHTML)에 string 변수를 때려넣는 습관은 지양하고 sanitizing 방법을 찾아 활용하는 것이 좋다.
깔끔하고 좋은 내용 잘 보고 갑니다!