はじめに
この記事では、HTML (DOM) の XSS 対策用のライブラリである DOMPurify の React での利用方法について記載します。
開発環境
開発環境は以下の通りです。
- Windows 11
- Chrome 131
- TypeScript 5.5.3
- React 18.3.1
- Vite 5.4.7
- Node.js 20.13.1
- npm 10.8.1
- DOMPurify 3.2.3
インストール
まずは DOMPurify をインストールします。
npm i dompurify
DOMPurify を利用しないケース
比較のため、DOMPurify を利用せずに React の dangerouslySetInnerHTML
に script タグを渡した時の挙動を確認します。
Unsafe.tsx
import { FC } from "react";
export const Unsafe: FC = () => {
const potentiallyUnsafeHTML = `
<div>
<h2>Hello World!</h2>
<script>alert('This is unsafe!');</script>
</div>
`;
return (
<div>
<h2>Without DOMPurify</h2>
<div dangerouslySetInnerHTML={{ __html: potentiallyUnsafeHTML }} />
</div>
);
};
Chrome の開発者ツールの Elements タブを確認すると、script タグが表示されます。
DOMPurify を利用するケース
DOMPurify を利用する際は、DOMPurify.sanitize
の引数に追加する HTML を渡します。
Safe.tsx
import { FC } from "react";
import DOMPurify from "dompurify";
export const Safe: FC = () => {
const potentiallyUnsafeHTML = `
<div>
<h2>Hello World!</h2>
<script>alert("This is unsafe!");</script>
</div>
`;
const sanitizedHTML = DOMPurify.sanitize(potentiallyUnsafeHTML);
return (
<div>
<h2>With DOMPurify</h2>
<div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />
</div>
);
};
DOMPurify を利用すると、script タグは表示されません。
画面表示時だけでなく、画面操作時の script も同様に無効化できます。
- DOMPurify を利用しないケース
Unsafe.tsx
import { FC } from "react";
export const Unsafe: FC = () => {
const potentiallyUnsafeHTML = `
<div>
<button onclick="alert('Still unsafe!')">Click me</button>
</div>
`;
return (
<div>
<h2>Without DOMPurify</h2>
<div dangerouslySetInnerHTML={{ __html: potentiallyUnsafeHTML }} />
</div>
);
};
DOMPurify を利用しないと script が実行されます。
- DOMPurify を利用するケース
Safe.tsx
import { FC } from "react";
import DOMPurify from "dompurify";
export const Safe: FC = () => {
const potentiallyUnsafeHTML = `
<div>
<button onclick="alert('Still unsafe!')">Click me</button>
</div>
`;
const sanitizedHTML = DOMPurify.sanitize(potentiallyUnsafeHTML);
return (
<div>
<h2>With DOMPurify</h2>
<div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />
</div>
);
};
DOMPurify を利用すると script は実行されません。
タブナビング攻撃などの対象になる脆弱性を持つ target=”_blank”
も無効化してくれます。
- DOMPurify を利用しないケース
Unsafe.tsx
import { FC } from "react";
export const Unsafe: FC = () => {
const potentiallyUnsafeHTML = `
<div>
<a href="https://example.com" target="_blank">
Visit Example
</a>
</div>
`;
return (
<div>
<h2>Without DOMPurify</h2>
<div dangerouslySetInnerHTML={{ __html: potentiallyUnsafeHTML }} />
</div>
);
};
- DOMPurify を利用するケース
Safe.tsx
import { FC } from "react";
import DOMPurify from "dompurify";
export const Safe: FC = () => {
const potentiallyUnsafeHTML = `
<div>
<a href="https://example.com" target="_blank">
Visit Example
</a>
</div>
`;
const sanitizedHTML = DOMPurify.sanitize(potentiallyUnsafeHTML);
return (
<div>
<h2>With DOMPurify</h2>
<div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />
</div>
);
};