vue-dompurify-html 을 사용합니다.
vue2 - https://github.com/LeSuisse/vue-dompurify-html/tree/vue-legacy
vue3 - https://www.npmjs.com/package/vue-dompurify-html
v-html
은 vue에서 실제 HTML을 출력할때 사용하는 디렉티브
공지사항, 에러 메시지
vue 공식문서 v-html에 따르면, 신뢰할 수 있는 콘텐츠에서만 사용하라고 되어있습니다.
VS Code Eslint 플러그인에서도 XSS(크로스 사이트 스크립트)으로 이어질 수 있다고 경고합니다.
웹에 스크립트를 삽입하는 기법을 의미합니다.
낮은 난이도에 비해, 자바스크립트로 할 수 있는 모든 것을 할 수 있기 때문에 주의가 필요합니다.
백엔드, 프론트엔드 모두 막아야하는데, 이번 포스팅에서는 프론트엔드에서 막는 방법
에 대해 알아봅니다.
vue 2.6.14 버전 기준
node_modules/vue/src/platforms/web/compiler/directives/
에 들어가면 v-html, v-text에 대한 내용을 볼 수 있습니다.
즉, v-html이 innerHTML 기반이기 때문에 XSS 위협이 있음
<div> {{ message }} </dvi>
Vue가 여러분을 보호하기 위해 하는 일 에 따르면 템플릿도 textContent 처럼 escape 처리를 진행합니다.
escape - 유해한 부분을 치환
예시) <html>
- <html&rt;
sanitize - 유해한 부분을 제거
예시) <img srcset=",,,,,x">
- <img srcset=",,,,,x">
1) 개요
MDN에 따르면 innerHTML은 HTML 또는 마크업을 가져오거나 설정합니다.
<script>alert(1)</script>
안되는데요innerHTML - security_considerations 에 따르면, <script>
태그는 실행되지 않도록 지정합니다.
하지만, <script>
태그를 사용하지 않는 방법은 가능합니다.
appHtml.innerHTML = `<svg/onload=alert(1)>`
appHtml.innerHTML = `<img src='x' onerror='alert(1)'>`
스크립트가 실행됩니다.
<html>
<body>
<div id="app">
<h1>안녕하세요</h1>
<h3>1. textContent</h3>
<div class="app-text"></div>
<h3>2. innerHTML</h3>
<div class="app-html"></div>
</div>
<script>
const appText = document.querySelector(".app-text")
const appHtml = document.querySelector('.app-html')
appText.textContent = '<svg/onload=alert(1)>'
// innerHTML - 크로스 사이트 스크립팅
appHtml.innerHTML = `<svg/onload=alert(1)>`
// appHtml.innerHTML = `<img src='x' onerror='alert(1)'>`
</script>
</body>
</html>
<template>
<div v-html="message" />
</template>
<script>
export default {
name: 'TestPage',
data() {
return {
message: '<svg/onload=alert(1)>'
}
}
}
</script>
여기까지 잠시 정리하면
v-html
이 크로스 사이트 스크립팅 위험이 있는 이유는 innerHTML
기반으로 구성되어 있기 때문
innerHTML은 HTML 또는 마크업을 가져오거나 설정, <script>
태그는 허용하지 않지만, <script>
태그를 사용하지 않는 경우 크로스 사이트 스크립팅 발생
appHtml.innerHTML = `<svg/onload=alert(1)>`
appHtml.innerHTML = `<img src='x' onerror='alert(1)'>`
여러가지 이유로 v-html은 써야하는데 크로스사이트 스크립팅은 막아야한다.
-> 필터를 적용하자
태그 이스케이프하고, 공부좀 하고 만들까 싶기도 했었는데 Danger of using v-html in Vue applications 이분 말씀에 따르면
전문가가 아니면, 보안 알고리즘을 절대 구현하면 안된다.
그래서 결과는 vue-dompurify-html
을 사용하자
vue-dompurify-html
은 dompurify
를 적용하여 v-html에 필터를 적용하는 디렉티브입니다.
일단 두개의 라이브러리 모두 꾸준이 업데이트 되고 있고, 2022.06.01
기준 비교적 최근 19일전에 업데이트 되었고, 다운로드수도 많고 믿을만하다고 판단합니다.
적용 방법은 정말 간단합니다.
// vue 2 이하
npm install vue-dompurify-html@vue-legacy
// vue 3 이상
npm i vue-dompurify-html
// main.js
import Vue from 'vue'
import App from './App.vue'
import VueDOMPurifyHTML from 'vue-dompurify-html'
Vue.use(VueDOMPurifyHTML)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
하나씩 바꿀 생각하지 말고, VS Code의 replace all
기능을 사용해서 프로젝트 전체에서 일괄 변경한다.
위협이 안되는 내용은 그대로 나오고
위협이 될만한 부분은 지워버립니다.
참고로 다음과 같이 부릅니다.
이스케이프 - 유해한 부분을 치환
예시) <html>
- <html&rt;
sanitize - 유해한 부분을 제거
예시) <img srcset=",,,,,x">
- <img srcset=",,,,,x">
HTML5 Security Cheatsheet 여기 말고도 엄청나게 많은 테스트 케이스가 있는데 하나씩 해보려고
아래의 있는 내용은 전부 통과했습니다.
1. 즉시 실행
`<svg/onload=alert(1)>`
`<video><source onerror="alert(1)">`
`<iframe srcdoc="<img src=x:x onerror=alert(1)>" />`
`<picture><source srcset="x"><img onerror="alert(1)"></picture>`
`<picture><img srcset="x" onerror="alert(1)"></picture>`
`<img srcset=",,,,,x" onerror="alert(1)">`
2. 액션
`<form id="test"></form><button form="test" formaction="javascript:alert(1)">X</button>`
`<input onfocus=alert(1) autofocus>`
`<input onblur=alert(1) autofocus><input autofocus>`
`<form><button formaction="javascript:alert(1)">X</button>`
`<iframe srcdoc="<svg onload=alert(1)>⃒"></iframe>`
`<a href="javascript:'<svg onload=alert(1)>⃒'">CLICK</a>`
XSS 실습 사이트
엄한데 가서 하지말고, 여기서 테스트
https://www.acunetix.com/blog/web-security-zone/test-xss-skills-vulnerable-sites/