[vue] v-html은 사용하고 싶지만, 크로스 사이트 스크립팅은 피하고 싶어

skyepodium·2022년 6월 1일
4

1. 결론

vue-dompurify-html 을 사용합니다.

vue2 - https://github.com/LeSuisse/vue-dompurify-html/tree/vue-legacy

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

2. v-html

1) 정의

v-html은 vue에서 실제 HTML을 출력할때 사용하는 디렉티브

2) 실제로 v-html을 사용하는 경우

공지사항, 에러 메시지

3) 가이드

vue 공식문서 v-html에 따르면, 신뢰할 수 있는 콘텐츠에서만 사용하라고 되어있습니다.

VS Code Eslint 플러그인에서도 XSS(크로스 사이트 스크립트)으로 이어질 수 있다고 경고합니다.

4) 크로스 사이트 스크립팅

웹에 스크립트를 삽입하는 기법을 의미합니다.

낮은 난이도에 비해, 자바스크립트로 할 수 있는 모든 것을 할 수 있기 때문에 주의가 필요합니다.

응용분야

  • 쿠키 탈취후 어드민 계정으로 로그인
  • 키로깅

5) 막는법

백엔드, 프론트엔드 모두 막아야하는데, 이번 포스팅에서는 프론트엔드에서 막는 방법에 대해 알아봅니다.

6) 왜 v-html 만 XSS에 걸리나요?

vue 2.6.14 버전 기준
node_modules/vue/src/platforms/web/compiler/directives/에 들어가면 v-html, v-text에 대한 내용을 볼 수 있습니다.

  • v-html - innerHTML - HTML 또는 마크업을 가져오거나 설정
  • v-text - textContent - 노드와 그 자손의 텍스트 콘텐츠를 표현, 꺽쇠 태그 escape

즉, v-html이 innerHTML 기반이기 때문에 XSS 위협이 있음

7) 템플릿

<div> {{ message }} </dvi>

Vue가 여러분을 보호하기 위해 하는 일 에 따르면 템플릿도 textContent 처럼 escape 처리를 진행합니다.

8) 참고

escape - 유해한 부분을 치환
예시) <html> - &lt;html&rt;

sanitize - 유해한 부분을 제거
예시) <img srcset=",,,,,x"> - <img srcset=",,,,,x">

3. innerHTML

1) 개요
MDN에 따르면 innerHTML은 HTML 또는 마크업을 가져오거나 설정합니다.

2) <script>alert(1)</script> 안되는데요

innerHTML - security_considerations 에 따르면, <script> 태그는 실행되지 않도록 지정합니다.

하지만, <script> 태그를 사용하지 않는 방법은 가능합니다.

appHtml.innerHTML = `<svg/onload=alert(1)>`
appHtml.innerHTML = `<img src='x' onerror='alert(1)'>`

4. innerHTML - XSS 예시

1) 바닐라 js 예시

스크립트가 실행됩니다.

<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>

2) vue 예시

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

<script>
export default {
    name: 'TestPage',
    data() {
        return {
            message: '<svg/onload=alert(1)>'
        }
    }
}
</script>

5. 잠시 정리

여기까지 잠시 정리하면

1) v-html

v-html 이 크로스 사이트 스크립팅 위험이 있는 이유는 innerHTML 기반으로 구성되어 있기 때문

2) innerHTML

innerHTML은 HTML 또는 마크업을 가져오거나 설정, <script> 태그는 허용하지 않지만, <script> 태그를 사용하지 않는 경우 크로스 사이트 스크립팅 발생

appHtml.innerHTML = `<svg/onload=alert(1)>`
appHtml.innerHTML = `<img src='x' onerror='alert(1)'>`

3) 고민

여러가지 이유로 v-html은 써야하는데 크로스사이트 스크립팅은 막아야한다.

-> 필터를 적용하자

6. 리서치

1) 직접 만들까?

태그 이스케이프하고, 공부좀 하고 만들까 싶기도 했었는데 Danger of using v-html in Vue applications 이분 말씀에 따르면

전문가가 아니면, 보안 알고리즘을 절대 구현하면 안된다.

2) vue-dompurify-html

그래서 결과는 vue-dompurify-html을 사용하자

vue-dompurify-htmldompurify를 적용하여 v-html에 필터를 적용하는 디렉티브입니다.

일단 두개의 라이브러리 모두 꾸준이 업데이트 되고 있고, 2022.06.01 기준 비교적 최근 19일전에 업데이트 되었고, 다운로드수도 많고 믿을만하다고 판단합니다.

7. vue-dompurify-html 적용

1) 설치 및 적용

적용 방법은 정말 간단합니다.

// 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 기능을 사용해서 프로젝트 전체에서 일괄 변경한다.

2) 확인

위협이 안되는 내용은 그대로 나오고

위협이 될만한 부분은 지워버립니다.

참고로 다음과 같이 부릅니다.

  • 이스케이프 - 유해한 부분을 치환
    예시) <html> - &lt;html&rt;

  • sanitize - 유해한 부분을 제거
    예시) <img srcset=",,,,,x"> - <img srcset=",,,,,x">

3) 테스트 케이스

HTML5 Security Cheatsheet 여기 말고도 엄청나게 많은 테스트 케이스가 있는데 하나씩 해보려고

아래의 있는 내용은 전부 통과했습니다.

1. 즉시 실행

`<svg/onload=alert(1)>`

`<video><source onerror="alert(1)">`

`<iframe srcdoc="&lt;img src&equals;x:x onerror&equals;alert&lpar;1&rpar;&gt;" />`

`<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)&nvgt;"></iframe>`

`<a href="javascript:&apos;<svg onload&equals;alert&lpar;1&rpar;&nvgt;&apos;">CLICK</a>`

참고

https://medium.com/@bsalwiczek/danger-of-using-v-html-in-vue-applications-see-better-approach-3def415ba32b

https://web.dev/sanitizer/

XSS 실습 사이트
엄한데 가서 하지말고, 여기서 테스트
https://www.acunetix.com/blog/web-security-zone/test-xss-skills-vulnerable-sites/

profile
callmeskye

0개의 댓글