【実務・中級編】WebAssembly(Wasm)におけるセキュリティ境界とXSS – アプリケーションセキュリティ & 安全な開発防御ガイド

Wasmは「聖域」ではない:ブラウザのメモリ安全性を逆手に取ったXSSの現実

現場でコードを叩いていると、「WebAssembly(Wasm)を使っているから、うちはJavaScriptのXSSとは無縁だ」という、あまりにも危うい誤解を耳にすることがある。

確かにWasmはバイナリ形式であり、メモリ安全性を考慮した設計になっている。しかし、Wasmは孤立した宇宙ではない。ブラウザのDOMを操作しようとした瞬間、その「安全なはずの領域」から、JavaScriptの脆弱性という現実世界へ橋を架けることになる。今日は、Wasmを導入したシステムで起きるインジェクションの盲点と、どう防御すべきかという泥臭い話をしよう。

—

Wasmにおける「境界」の正体

Wasmがブラウザ上で動くとき、それは「線形メモリ(Linear Memory)」という閉じた領域で処理される。CやRustで書かれたコードはここで爆速で動くが、DOMを操作する際は、必ずJavaScriptの接着剤(Glue Code)を経由してブラウザのAPIを叩く必要がある。

攻撃者が狙うのは、まさにこの「接着剤」の部分だ。

Wasmモジュールが生成した出力結果が、サニタイズされないまま innerHTML や document.write に渡されたらどうなるか。たとえ内部ロジックが完璧でも、最終的な描画エンジンに渡る段階で、XSSのトリガーが引かれる。Wasmは「計算処理」には強いが、「コンテキストに応じた出力エスケープ」の責任は、結局のところJS側に残るというわけだ。

—

実践的PoC:Wasmから仕込むXSSの構図

例えば、ユーザー入力をWasmで加工して表示する機能を想像してほしい。

1. Wasm側: ユーザー入力を受け取り、文字列を加工してJS側に返す。
2. JS側: Wasmから受け取った文字列を、何の疑いもなくDOMに挿入する。

// 【脆弱な実装例】JS側の接着コード
async function renderWasmOutput(userInput) {
const wasm = await import(‘./processor.wasm’);
const result = wasm.process_data(userInput);

// 致命的な脆弱性: Wasmが返した値をそのままDOMに流し込んでいる
document.getElementById(‘display’).innerHTML = result;
}

もし、Wasmの process_data が入力値を適切に処理せず、 のようなペイロードをそのまま返してしまったら? Wasmモジュールがどれほど堅牢な言語で書かれていようが、ブラウザにとってはただの「悪意あるHTML文字列」でしかない。

—

完全防御のためのセキュア実装:WasmとDOMの間に「壁」を築く

防御の鉄則は「Wasm側で信頼せず、JS側で必ずエスケープする」ことだ。WasmとJSの境界線で、データの性質を厳格に定義する。

1. JS側での徹底したエスケープ(推奨)

Wasmが何を返そうとも、DOM操作の直前で安全なメソッドを使用する。innerHTML は禁じ手だ。

// 【セキュアな実装例】JS側でDOM挿入を制御する
function secureRender(wasmResult) {
const displayElement = document.getElementById(‘display’);

// 危険: displayElement.innerHTML = wasmResult;

// 安全: textContentを使用すれば、ブラウザは文字列としてのみ解釈する
displayElement.textContent = wasmResult;
}

2. CSP(Content Security Policy)による多重防御

万が一、DOM操作のコードに脆弱性が潜んでいたとしても、CSPで実行をブロックする。Wasmの実行には unsafe-eval が必要になるケースがあるが、可能な限り制限をかけよう。

Nginxの設定例: CSPヘッダーでインラインスクリプトを封じ込める
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’ ‘wasm-unsafe-eval’; object-src ‘none’;”;

※ wasm-unsafe-eval を使用する際は、インラインスクリプトや eval() が混入しないよう、厳格なポリシー運用が求められる。

—

現場で生き残るための「セキュリティ・マインドセット」

多くのエンジニアが陥る罠は、「技術の先進性がセキュリティを担保してくれる」という幻想だ。Wasmはパフォーマンスの革命だが、セキュリティの免罪符ではない。

もし君が開発リーダーなら、チームに以下のルールを徹底させてほしい。

1. 境界分離の原則: Wasmモジュールは「データ処理機」と割り切り、HTML生成の責任は負わせない。
2. 型安全の徹底: WasmからJSへデータを渡す際は、プリミティブな型(数値や、エスケープ済みの文字列)のみを許可する。
3. 自動テストにXSSパターンを含める: 「Wasmを使っているから大丈夫」というテストコードは今すぐ破棄し、あえて悪意あるHTML断片をWasmに注入し、JS側で正しくエスケープされているか確認するE2Eテストを実装すること。

セキュリティとは、最新技術を追いかけることではなく、「データが流れる場所には必ず落とし穴がある」という疑念を常に持ち続けることだ。Wasmを使おうが何を使おうが、ブラウザのDOMがHTMLを解釈する以上、我々の戦い方は変わらない。堅牢な設計で、泥臭く守り抜こう。

コメント

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