【実務・中級編】SVG画像ファイルに埋め込まれたXSSの脅威と検証手法 – アプリケーションセキュリティ & 安全な開発防御ガイド

SVGは「ただの画像」ではない:悪魔が潜むベクター画像の脅威と防御策

こんにちは。現場で泥をすすりながらインシデント対応をしてきた身から言わせてもらえば、「画像アップロード機能」は、Webアプリにおける最大の鬼門の一つです。

多くのエンジニアは「拡張子が .jpg か .png かチェックしているから大丈夫」と胸をなでおろしますが、そこに .svg が混ざった瞬間、セキュリティの境界線は跡形もなく崩れ去ります。なぜなら、SVGは画像ではなく、「実行可能なXMLドキュメント」だからです。

今日は、SVGに潜むXSS(クロスサイトスクリプティング)の罠と、それを実務レベルで完封するための実装テクニックを共有します。

—

1. なぜSVGは危険なのか:攻撃のメカニズム

SVG(Scalable Vector Graphics)は、ブラウザが直接解釈して描画するXML形式の画像です。ここには、HTMLと同様にスクリプトを埋め込むための「遊び」が用意されています。

攻撃者が仕掛ける罠(PoC)

攻撃者は、一見無害なロゴやアイコンのSVGファイルの中に、以下のようなコードを忍ばせます。


もしこのファイルをユーザーがアップロードし、Webアプリがそれを タグや直接のリンクとして表示した場合、ブラウザはそのSVGをレンダリングするタイミングで onload イベントを発火させます。 つまり、閲覧者のブラウザ上で攻撃者のJavaScriptが実行されるわけです。これがSVGによるXSSの正体です。

—

2. 実務で使える防御策:3つの防波堤

単なる拡張子チェックは無意味です。以下の3層構造で守りを固めます。

防御策①:Content-Typeの強制とContent-Disposition

SVGをブラウザが「スクリプトを含むドキュメント」として解釈しないよう、ヘッダーで制御します。特に重要なのは Content-Disposition: attachment です。これにより、ブラウザはSVGを「開く」のではなく「ダウンロード」させるようになります。

Nginxの設定例:

SVGファイルへのリクエストに対するセキュリティヘッダー
location ~ \.svg$ {
# 実行を阻止するために強制的にダウンロードさせる
add_header Content-Disposition ‘attachment; filename=”image.svg”‘;
# ブラウザがMIMEタイプを勝手に推測するのを防ぐ
add_header X-Content-Type-Options ‘nosniff’;
}

防御策②:SVGの正規化とサニタイズ(PHP実装例)

アップロードされたSVGをそのまま保存するのは自殺行為です。DOMDocument を使用して、スクリプトに関連するタグや属性を徹底的に削除します。

  • SVGをサニタイズする関数
  • /
    function sanitizeSvg($filePath) {
    $dom = new DOMDocument();
    $dom->load($filePath);

    // 危険なタグを削除
    $badTags = [‘script’, ‘foreignObject’];
    foreach ($badTags as $tag) {
    $nodes = $dom->getElementsByTagName($tag);
    foreach ($nodes as $node) {
    $node->parentNode->removeChild($node);
    }
    }

    // 危険な属性(on系イベントハンドラ)を削除
    $xpath = new DOMXPath($dom);
    $nodes = $xpath->query(‘//[@[starts-with(name(), “on”)]]’);
    foreach ($nodes as $node) {
    foreach ($node->attributes as $attr) {
    if (strpos($attr->name, ‘on’) === 0) {
    $node->removeAttribute($attr->name);
    }
    }
    }

    $dom->save($filePath);
    }

    防御策③:CSP(Content Security Policy)でトドメを刺す

    万が一、サニタイズをすり抜けても、CSPで「インラインスクリプトの実行」を禁止していれば攻撃は不発に終わります。

    HTTPヘッダーのCSP設定
    Content-Security-Policy: default-src ‘self’; script-src ‘self’; object-src ‘none’;

    object-src 'none' を設定することで、SVG内の

    securityintronationalをフォローする

    コメント

    タイトルとURLをコピーしました