React를 사용하다 보면 간단한 HTML 태그, 예를 들어<b>
나 <a>
같은 링크를 포함한 텍스트를 직접 렌더링해야 하는 상황이 생깁니다.
const text = '궁금하시다면 <a href="https://www.google.com">매뉴얼</a>을 참고해 주세요.';
return <p dangerouslySetInnerHTML={{ __html: text }} />; // 이렇게 하면 텍스트 내부의 `<a>` 태그가 실제 링크로 렌더링됩니다.
dangerouslySetInnerHTML
은 브라우저 DOM에서 innerHTML을 사용하기 위한 React의 대체 방법입니다. 일반적으로 코드에서 HTML을 설정하는 것은 사이트 간 스크립팅 공격(XSS: Cross Site Scripting)에 쉽게 노출될 수 있기 때문에 위험합니다. 따라서 React에서 직접 HTML을 설정할 수는 있지만, 위험하다는 것을 상기시키기 위해 dangerouslySetInnerHTML
을 작성하고 __html 키로 객체를 전달해야 합니다.
오늘은 DOMPurify를 이용하여 XSS 공격을 효과적으로 방어하는 방법을 소개하겠습니다.
XSS란 무엇인가?
XSS(Cross Site Scripting)는 웹 애플리케이션의 보안 취약점을 이용하여 사용자의 브라우저에서 악성 스크립트(주로 JavaScript)를 실행하는 공격입니다. 이 공격은 사용자 세션을 탈취하거나 쿠키 정보, 개인 정보 등의 민감한 데이터를 도용하는 데 사용될 수 있습니다.
왜 dangerouslySetInnerHTML이 위험한가?
dangerouslySetInnerHTML
은 HTML 문자열을 그대로 DOM에 삽입하는 기능을 제공하는데, 악의적인 사용자가 입력한 JavaScript 코드가 함께 실행될 수 있습니다.
XSS가 어떻게 발생할 수 있는지?
(혹시 몰라..)
⚠️ 주의: 예제 코드 및 방법은 오직 학습 목적입니다. 로컬 개발 환경에서만 테스트해야 하며, 실제 서비스나 타인의 웹사이트에서는 절대 사용해서는 안 됩니다.
예를 들어, 사용자가 _소개글/게시판/댓글/마크다운 등_에 다음과 같은 HTML을 입력했다고 가정해 봅니다:
<img src="x" onerror="alert('내 정보가 노출될 수 있어요!')" />
관리자나 다른 사용자가 해당 사용자의 프로필 페이지를 열었을 때, 이 HTML이 그대로 출력되고 자바스크립트가 실행된다면 XSS 공격이 발생합니다. 공격자는 이 alert 대신 사용자의 쿠키를 훔치거나, 악성 사이트로 리다이렉션 하는 등 실제 피해를 줄 수 있는 행동도 할 수 있습니다.
function PostContent({ userInput }) {
return <p dangerouslySetInnerHTML={{ __html: userInput }} />;
}
이처럼 사용자 입력을 정제하지 않고 DOM에 삽입하면, 의도치 않게 스크립트가 실행되어 보안 문제가 발생할 수 있습니다. 이 문제를 방지하기 위해 DOMPurify와 같은 라이브러리를 사용해 입력을 정제(sanitize)하는 것이 중요합니다.
실습 예제가 잘 나와 있는 블로그를 발견해서 공유드립니다!
DOMPurify를 활용한 방어 방법
XSS 공격을 막는 여러 방법 중에 DOMPurify 라이브러리를 사용하는 방법입니다.
DOMPurify는 HTML "살균(sanitize)"하여 악의적인 코드나 속성을 제거하는 JavaScript 라이브러리입니다.
DOMPurify를 이용하면 위의 예제도 안전하게 처리할 수 있습니다.
먼저 DOMPurify를 설치합니다.
npm install dompurify
그리고 React 컴포넌트에서 다음과 같이 사용합니다:
import { useState } from 'react';
import DOMPurify from 'dompurify';
function SafeComponent() {
const [userInput, setUserInput] = useState('');
const cleanHTML = DOMPurify.sanitize(userInput);
return (
<div>
<input
type="text"
value={userInput}
onChange={(e) => setUserInput(e.target.value)}
placeholder="HTML을 입력하세요"
/>
<div dangerouslySetInnerHTML={{ __html: cleanHTML }} />
</div>
);
}
이제 같은 HTML 입력이라도 DOMPurify가 위험한 속성을 제거하여, 안전하게 HTML을 렌더링할 수 있습니다.
DOMPurify 추가 설정
DOMPurify는 세부적인 옵션 설정도 가능합니다. 특정 태그와 속성만 허용하도록 설정할 수도 있습니다.
const cleanHTML = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href']
});
이렇게 하면 지정된 태그와 속성만 남기고 나머지는 모두 제거하여 더욱 안전한 HTML을 만들 수 있습니다.
SSR(Server-Side Rendering)에서 DOMPurify 사용
Next.js 등 SSR 환경에서도 DOMPurify를 문제없이 사용할 수 있지만, DOMPurify가 DOM API를 사용하기 때문에 서버 환경(Node.js, nextjs)에서는 별도의 설정이 필요할 수 있습니다. 대표적인 방법은 jsdom을 이용하는 것입니다:
import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
const window = new JSDOM('').window;
const purify = DOMPurify(window);
export function sanitizeHTML(html) {
return purify.sanitize(html);
}
React에서는 dangerouslySetInnerHTML
사용 시 반드시 XSS 위험을 인지하고, DOMPurify와 같은 보안 도구로 사용자 입력을 정제해야 합니다. 이 글이 여러분의 안전한 프론트엔드 개발에 도움이 되었기를 바랍니다. 🚀
'Frontend > Development' 카테고리의 다른 글
Bun: 빠르고 혁신적인 JavaScript 런타임Bun이란? (0) | 2025.02.16 |
---|---|
React 18, 19: 새롭게 나온 Hook 소개 (0) | 2025.01.19 |
javascript 이미지 색상 추출하기 (0) | 2024.08.11 |
내부 요소 스크롤 위치 도달 시 부모 요소 스크롤 방지하기 (0) | 2024.05.28 |
useLoaderData와 useRouteLoaderData의 사용법과 차이점 (2) | 2023.12.31 |