SVGは「画像」にあらず。そのアップロード機能が招くXSSの深淵
現場でよくある光景だ。「ユーザーがプロフィールアイコンをアップロードできるようにしたい」という機能要望に対し、開発者が深く考えずに「画像ならOK」と拡張子チェックだけで実装を完了させてしまう。
しかし、現代のWebアプリケーションにおいて、SVGを単なる「画像」として扱うのは、「爆弾の導火線に火をつけながら、これはただの紐です」と主張するに等しい。
SVG(Scalable Vector Graphics)は画像ファイルではない。あれはXMLであり、DOMを内包し、JavaScriptを実行できる「Webドキュメント」だ。今日は、SVGを介したXSS(クロスサイトスクリプティング)のメカニズムと、泥臭い現場で生き残るための防御策について話をしよう。
—
1. なぜSVGでXSSが成立するのか?
SVGファイルの中にタグを埋め込むことは、仕様上許されている。以下は、攻撃者が送り込む悪意あるSVGのPoC(概念実証)だ。
このファイルを、WebサーバーがContent-Type: image/svg+xmlで返却し、かつブラウザがその画像に直接アクセス(またはタグでインライン表示)すると、ブラウザは「これは画像だ」と認識しつつも、SVG内のJavaScriptをコンテキスト上で実行してしまう。
特に危険なのは、「サーバーサイドで画像サイズをチェックしたから大丈夫」という誤解だ。ファイルの内容をパースせずにバイナリサイズだけを見ているなら、そのセキュリティ対策はザルと同じだ。
---
2. 現場で使える防御戦略:多層防御の鉄則
SVGのアップロードを許可する場合、以下の3つを組み合わせて「完全に無害化」する必要がある。
1. Content-Security-Policy (CSP) の厳格化: ブラウザ側でスクリプトの実行を禁止する。
2. DOMPurifyによるサニタイズ: XMLをパースし、タグやonイベントハンドラを除去する。
3. Content-Dispositionヘッダの強制: ブラウザに直接レンダリングさせず、強制ダウンロードさせる。
実装例:Python (Django/Flask) でのサニタイズ
サーバーサイドでSVGを受け取った際、ライブラリを使って「有害な要素」を削ぎ落とすのが定石だ。Pythonならdefusedxmlとlxmlの組み合わせ、あるいはサニタイズ専用のライブラリが必須となる。
pip install lxml
from lxml.html.clean import Cleaner
def sanitize_svg(svg_content):
# scriptタグやon属性を削除するためのクリーナー
cleaner = Cleaner(scripts=True, javascript=True, comments=True, style=True)
# XMLとしてパースし、クリーンアップ
# ※本番ではlxml.etreeでXMLバリデーションも行うこと
try:
sanitized_svg = cleaner.clean_html(svg_content)
return sanitized_svg
except Exception as e:
# パース失敗時は拒否する(攻撃の可能性があるため)
raise ValueError("Invalid SVG format")
インフラ・設定レベルでの対策:Nginx
ファイル配信時には、ブラウザがスクリプトを実行しないように強制するのがセキュリティチーフの務めだ。
アップロードされたディレクトリに対する設定
location /uploads/ {
# 1. ブラウザにHTMLとしてレンダリングさせない
add_header Content-Disposition "attachment; filename=\"image.svg\"";
# 2. XSSフィルターの強制
add_header X-Content-Type-Options "nosniff";
# 3. CSPによるスクリプト実行ブロック
# 'unsafe-inline' を含めないことが重要
add_header Content-Security-Policy "default-src 'none'; script-src 'none'; object-src 'none';";
}
---
3. 最後に:エンジニアが持つべき「疑いの心」
SVGに限らず、ファイルアップロード機能はアプリケーションにおける「最大の侵入口」の一つだ。今回の話をまとめると、以下のチェックリストを常に自分自身に突きつけてほしい。
- 「画像だから安全」というバイアスを捨てているか?
- ファイルの中身(XML構造)をバリデーションしているか?
- Content-Typeをサーバー側で厳格に制御しているか?
- CSPヘッダで「スクリプト実行不可」を明示しているか?
最も強力な対策は、「SVGのアップロードを許可しない」ことだ。もし要件として外せないなら、上記のような厳格なサニタイズとCSPの適用をセットで行うこと。妥協は、いつか必ず致命的なインシデントとなって自分たちの元へ帰ってくる。
セキュリティは、派手なハッキング技術を止めることではなく、こうした「当たり前の実装」を地道に積み重ねることで守られる。頑張ってくれ。君たちのコードが、明日も安全であることを願っている。
コメント