MarkdownをHTMLに変換する時、あなたのWebサイトは「見えない泥棒」を招き入れていませんか?
こんにちは。セキュリティの現場で長年、泥臭いインシデントと向き合ってきた筆者です。
今日は、開発現場でよく使う「Markdown(マークダウン)」の意外な落とし穴についてお話しします。「Markdownならタグを直接書かないから安全でしょ?」と思っているあなた。実は、その油断こそが攻撃者の最大の狙い目なんです。
「家を建てるとき、玄関の鍵をかけたはずなのに、実は窓から誰でも入れる状態だった」……そんな恐ろしい事態を、Web開発の世界で防ぐ方法を一緒に見ていきましょう。
—
1. なぜMarkdownが危険なのか?(泥棒の侵入経路)
Markdownは、# 見出し や [リンク](url) といったシンプルな記法でHTMLを生成する便利なツールです。しかし、中には「HTMLタグをそのまま許容する」という仕様を持つパーサー(変換器)があります。
例えば、攻撃者がMarkdown入力欄にこんなコードを仕込んだらどうなるでしょう。
[クリックしてね](javascript:alert(‘あなたのCookieを盗みました!’))
あるいは、もっと巧妙に:
これがHTMLに変換されると、ブラウザは「ああ、これはただの画像表示の失敗処理だね、実行しなきゃ!」と勘違いして、攻撃者のスクリプトを走らせてしまいます。これがXSS(クロスサイトスクリプティング)という攻撃です。
家の防犯に例えるなら、「合鍵を渡した覚えはないのに、手紙の隙間から細工された針金を差し込まれて鍵を開けられる」ようなものです。入り口(Markdown入力欄)を信頼しきっていると、中身がどんなに強固な金庫であっても無意味になってしまいます。
—
2. 鋼鉄の門番「DOMPurify」を設置する
では、どうすればいいのでしょうか? 答えは簡単。「入力されたものを、一度すべて洗浄(サニタイズ)する」ことです。
ここで登場するのが、業界標準の浄化ライブラリ「DOMPurify」です。これは、HTMLの中から「危ない要素(タグやonerror属性など)」だけを、まるで泥棒が持っているピッキングツールだけを没収するように、綺麗に取り除いてくれる名門の門番です。
実装のコード例
Node.jsやブラウザ環境でMarkdownをHTMLに変換する際は、以下のようなパイプラインを構築しましょう。
import { marked } from 'marked'; // MarkdownをHTMLに変換するライブラリ
import DOMPurify from 'dompurify'; // 悪意のあるコードを洗浄するライブラリ
import { JSDOM } from 'jsdom'; // Node.js環境でDOMを模倣するためのもの
// 1. まずMarkdownを安全なHTMLに変換する準備
const window = new JSDOM('').window;
const purify = DOMPurify(window);
function safeMarkdownToHtml(markdownText) {
// ① まずMarkdownをHTMLに変換(まだこの時点では危険!)
const rawHtml = marked.parse(markdownText);
// ② DOMPurifyで「危険な部品」を徹底的に除去(これが重要!)
const cleanHtml = purify.sanitize(rawHtml);
return cleanHtml;
}
// 使用例
const userEntry = "";
console.log(safeMarkdownToHtml(userEntry));
// 出力結果: (危険なonerror属性が綺麗に消えています!)
---
3. 多層防御:HTTPレスポンスヘッダーという「防犯カメラ」
DOMPurifyで掃除をするのは「玄関の鍵」ですが、もしもの時のために「防犯カメラ」も設置しておきましょう。それがCSP(Content Security Policy)というHTTPヘッダーです。
サーバーからブラウザに対して、「このサイトでは外部からのスクリプト実行を許可しない!」と厳しく命令を出す仕組みです。
例えば、Webサーバーの設定ファイル(NginxやApacheなど)で、以下のようなヘッダーを付与します。
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
- default-src 'self': 自分のサーバーにあるもの以外は読み込まない。
- script-src 'self': スクリプトも自分のサーバーのもの以外は実行しない(インラインスクリプトも禁止)。
- object-src 'none': Flashなどの古いプラグインを無効化する。
こうすることで、万が一DOMPurifyをすり抜けるような未知の脆弱性があったとしても、ブラウザが「いや、このスクリプトは許可リストにないから実行できないよ!」と止めてくれるようになります。
---
最後に:セキュリティは「疑う」ことから始まる
新人の皆さんに一番伝えたいのは、「ユーザーが入力するものは、すべて悪意があるものだと思え」という金言です。
- Markdownパーサーは本当に安全なオプション設定になっていますか?
- 出力する前に、DOMPurifyのような信頼できるライブラリを通しましたか?
- もしもの時のために、CSPで二重のブロックをかけましたか?
これらを一つずつ確認するだけで、あなたのWebアプリケーションの堅牢性は劇的に向上します。セキュリティ対策は、一度やって終わりではありません。日々の開発の中に、少しずつ「泥棒を入れない習慣」を組み込んでいきましょう。
大丈夫、一歩ずつ学べば必ず強くなれます。何かあればいつでも相談してくださいね。現場からは以上です!
コメント