FlawOpen

flawopen.com/JavaScript XSS/Vue v-html

Vue v-html XSS 위험과 안전한 사용법

참고 페이지
쉽게 설명하면

Vue의 {{ comment }} 보간은 인용된 쪽지를 유리 너머로 보여주는 것과 같습니다. 꺾쇠도 텍스트로 남습니다. v-html은 브라우저에 쪽지를 HTML로 열라고 요청합니다. 사용자가 마크업을 제어하면 브라우저가 이를 페이지 구조나 활성 콘텐츠로 처리해 XSS 위험을 만들 수 있습니다.

취약 — 사용자가 제공한 원시 HTML
<p v-html="comment"></p>
// comment가 실제 마크업으로 해석됨
수정됨 — 정제된 리치 HTML
<script setup>
import { computed } from 'vue'
import DOMPurify from 'dompurify'

const props = defineProps({ comment: String })
const safeComment = computed(() =>
  DOMPurify.sanitize(props.comment ?? '', {
    ALLOWED_TAGS: ['p', 'strong', 'em'],
    ALLOWED_ATTR: []
  })
)
</script>

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

v-html 전에 리치 HTML 정제

내용이 텍스트여야 한다면 Vue의 {{ }} 보간을 우선 사용하세요. 사용자 작성 리치 HTML이 꼭 필요한 기능에서는 DOMPurify 같은 유지 관리되는 라이브러리를 좁은 허용 목록으로 사용하고, 정제된 결과만 v-html에 바인딩하세요. 새니타이저를 최신 상태로 유지하고 허용 마크업 정책을 테스트하세요. 정제는 위험을 낮출 뿐 모든 HTML을 모든 맥락에서 안전하게 만들지는 않습니다. 신뢰할 수 없는 활성 콘텐츠를 표시해야 하면 격리된 iframe을 사용하세요. Vue는 v-html 내용을 Vue 템플릿으로 컴파일하지 않습니다.

v-html 감사 및 데이터 흐름 추적

모든 v-html 바인딩을 검색하세요. 각 항목의 출처, 새니타이저와 설정, 렌더링된 내용을 볼 수 있는 사용자를 확인합니다. 직접 사용자 입력, 저장된 댓글, CMS 필드, API 응답을 표시하고 일반 텍스트는 보간을 사용하는지 확인하세요.

grep -rn "v-html" --include="*.vue" .

자주 묻는 질문

React의 dangerouslySetInnerHTML과 같은 위험인가요?

둘 다 프레임워크의 일반 텍스트 이스케이프 경로를 우회해 HTML을 DOM에 삽입합니다. 리치 HTML이 필요하면 유지 관리되는 새니타이저를 사용하고 정제된 결과를 모든 맥락에서 안전하다고 간주하지 마세요.

참고 자료

함께 보기 크로스 사이트 스크립팅(XSS, CWE-79): 동작 원리와 언어별 방어 방법 — flawopen.com WKWebView loadHTMLString은 신뢰할 수 없는 콘텐츠에 안전한가요? 반사형·저장형·DOM 기반 XSS는 어떻게 다른가요? Android addJavascriptInterface 보안 위험 Angular XSS 방어: 안전한 기본값과 우회 API MIME sniffing XSS: Content-Type과 nosniff CSP와 출력 이스케이프: 둘 다 필요할까요?