SVGという名の「トロイの木馬」:XSSの死角とアーキテクチャによる制約
多くの開発者が、画像アップロード機能において「拡張子チェック」や「MIMEタイプ判定」だけで安心している。だが、実務の最前線に立つ我々からすれば、それは「鍵のついていない玄関に、派手な看板を出している」のと同じだ。特にベクター形式であるSVGは、実質的に「XMLドキュメント」そのものであり、単なる画像データではない。そこに埋め込まれたスクリプトが、ブラウザのコンテキストで実行されるリスクをアーキテクトが理解していないケースがあまりにも多い。
1. なぜSVGは「画像」の枠を超えてしまうのか
SVGの正体はDOMツリーだ。ブラウザがSVGを読み込む際、それは画像エンジンではなく、HTMLと同じパースエンジンを通る。つまり、タグやonload属性を埋め込めば、それは「画像」ではなく「実行可能なスクリプト」へと変貌する。
攻撃者が狙うのは、SVGを直接タグで表示させる場合だけではない。CDNやストレージのオリジンから直接SVGを読み込ませることで、ブラウザのSame-Origin Policy (SOP) をバイパスし、セッションCookieを盗み出したり、DOMベースのXSSをトリガーしたりする。これはもはや画像処理の脆弱性ではなく、「信頼できないドキュメントの実行」という設計上の欠陥である。
2. 現場で通用する「多層防御」の実装戦略
「アップロードを許可しない」のが理想だが、ビジネス要件上そうもいかない場合、以下の3層で防衛ラインを構築する。
A. DOMサニタイザーによる正規化(サーバーサイド)
単なる正規表現でのフィルタリングは、エンコーディングの差異(Base64や実体参照など)で容易に回避される。DOMパーサーを通し、ホワイトリストベースで要素を再構築するアプローチが必須だ。
Pythonのlxmlを用いたSVGサニタイズ例
from lxml import etree
def sanitize_svg(svg_content):
# 許可する要素と属性のホワイトリスト
allowed_tags = {'svg', 'g', 'path', 'circle', 'rect'}
parser = etree.XMLParser(remove_comments=True, remove_pis=True)
tree = etree.fromstring(svg_content.encode('utf-8'), parser)
for element in tree.iter():
# ホワイトリストにない要素は削除
if element.tag not in allowed_tags:
# 悪意あるスクリプトを含みそうな要素を再帰的に排除
element.getparent().remove(element)
continue
# 危険な属性(onload, onerrorなど)を徹底排除
for attr in list(element.attrib):
if attr.lower().startswith('on'):
del element.attrib[attr]
return etree.tostring(tree).decode('utf-8')
B. コンテンツ配信時の「強制無害化」
SVGを配信する際、ブラウザに「スクリプトとして解釈させるな」という強い命令をHTTPヘッダーで送る必要がある。
- Content-Disposition: attachment: ブラウザで直接開くのではなく、ダウンロードさせる。
- Content-Security-Policy (CSP): SVG配信専用のドメインを作り、そこで
script-src 'none'を強制する。
セキュリティヘッダー設定例
Content-Type: image/svg+xml
Content-Disposition: attachment; filename="safe.svg"
Content-Security-Policy: default-src 'none'; img-src 'self';
X-Content-Type-Options: nosniff
3. 生成AI時代の新たな脅威:プロンプト・インジェクションとの交差点
近年、生成AIがSVGを生成するケースが増えている。もしユーザーがAIに「面白いイラストを描いて」と頼み、AIが生成したSVGにバックドアとしてスクリプトが混入していたらどうなるか。
我々アーキテクトが意識すべきは、「AIの出力は常に未検証の入力である」という原則だ。AIが生成したSVGをそのまま公開ストレージに置くフローは極めて危険である。必ず自動化されたサンドボックス環境でSVGをレンダリングし、意図しない通信が発生していないか、DOMが不自然に変更されていないかを動的解析するガードレイルを設けるべきだ。
4. 最後に:セキュリティは「信頼」をハックする技術である
SVGのXSSは、技術的な脆弱性というよりは、「画像は安全であるはずだ」という我々の心理的なバイアスを突いてくる。
- 検証の徹底: SVGをバイナリとして扱うのではなく、XMLとしてパースし、構造を破壊して再構築する。
- 分離の徹底: SVGの配信元をメインサイトのドメインから分離する(サンドボックスドメインの利用)。
我々のようなセキュリティの専門家は、ツールやフレームワークのバージョンを追うだけでなく、「このファイルがブラウザという強力なエンジンに渡された時、どのようなステートマシンとして振る舞うか」を想像しなければならない。
インシデントは常に「想定外の場所」からやってくる。あなたが許可したその一枚のSVGが、翌朝の致命的な情報漏洩のトリガーにならないよう、今のうちに設計の再点検を強く推奨する。
コメント