盲点としての javascript: スキーム:ブラウザの解釈仕様と「ホワイトリスト」の真実
現場でコードレビューをしていると、今なお javascript: スキームを起点としたXSS脆弱性に遭遇する。OWASP Top 10が浸透し、フレームワークの自動エスケープが標準化した現代において、なぜこれほどまでにこの手のアナログな攻撃が生き残っているのか。
答えはシンプルだ。「ブラウザがURLとして解釈可能な文字列の境界線」を、多くのエンジニアが過小評価しているからに他ならない。
ブラウザのパーサーという「魔境」
href="javascript:alert(1)" という記述がなぜ実行されるのか。これはブラウザのURLパーサーが、RFC 3986で規定されたスキーム定義を解釈する際、特定の条件下で「実行可能なコンテキスト」へと遷移してしまう仕様に起因する。
特に厄介なのは、DOM型のXSSだ。フロントエンドで動的に生成される タグや の src 属性に、信頼できないソースからの入力が混入する。攻撃者は単なる文字列として入力を送り込むが、ブラウザはその文字列が javascript: で始まっていれば、それを「遷移先」ではなく「実行コード」としてメモリ上のJSエンジンへ引き渡す。これはメモリレイヤにおける実行フローのハイジャックであり、現代的なCSP(Content Security Policy)をバイパスする最も古典的かつ強力な手法の一つだ。
「ブラックリスト」という名の幻想
「javascript: を取り除けばいい」――この発想が脆弱性の温床だ。
攻撃者は j
avascript:alert(1) のように、ブラウザが正規化処理(Normalization)で除去する制御文字を混入させる。あるいは、エンコードを二重に施すことで、フィルタリングロジックをすり抜ける。
防御の鉄則は、「許可されたもの以外を排除する(ホワイトリスト)」ことにある。しかし、それすらもURLのプロトコル判定という深いレイヤで実装しなければ意味がない。
実装例:セキュアなURLバリデーターの設計
以下のコードは、単なる文字列置換ではない。ブラウザの挙動を模倣した正規化と、プロトコルのホワイトリスト検証を分離した堅牢な実装だ。
/
- セキュアなURL検証ユーティリティ
- 特定のプロトコルスキームのみを許可し、javascript: 擬似スキームを根絶する
/
const validateUrl = (unsafeUrl) => {
// 1. まずURLオブジェクトとしてブラウザに解釈させる
// ブラウザの仕様に則った正規化プロセスを通すことで、隠れた制御文字等を無効化する
let parsedUrl;
try {
parsedUrl = new URL(unsafeUrl);
} catch (e) {
// URLとして解釈できない不正な形式は即座に拒否
return null;
}
// 2. ホワイトリスト方式によるプロトコル検証
// http, https, mailto 等、ビジネス上必要なスキームのみに限定する
const allowedProtocols = [‘http:’, ‘https:’, ‘mailto:’];
if (allowedProtocols.includes(parsedUrl.protocol)) {
return parsedUrl.href;
}
// javascript: スキームはここで弾かれる
console.warn([Security Alert] Blocked attempt to use protocol: ${parsedUrl.protocol});
return null;
};
次世代の脅威:生成AIとプロンプトインジェクション
今、我々が直面しているのは、LLMが生成したコードの中に、この javascript: スキームが「自然な回答」として混入するリスクだ。
RAG(Retrieval-Augmented Generation)システムを構築する際、生成AIがWebページをクローリングして得た情報を要約し、それを動的なリンクとしてユーザーに提示するケースが増えている。AIは「ユーザーが求める機能性」を優先するため、悪意のある入力値に対しても「ユーザーが望むなら」と無防備に javascript: を出力することがある。
ここで必要なのが「ガードレイル」のアーキテクチャだ。
AIの出力したHTMLコンテンツをそのままレンダリングするのではなく、DOM Purifyのようなライブラリを中継し、ALLOWED_URI_REGEXP を明示的に設定するレイヤを一段挟む必要がある。
結論:泥臭い検証の積み重ねこそが防御の要
セキュリティにおいて「銀の弾丸」は存在しない。
javascript: スキームのフィルタリングは、Web技術の進化とともに常に形を変えていく。ブラウザのパーサー仕様、メモリ上の実行フロー、そして生成AIが持ち込む新たな文脈――これら全てを監視し、プロトコルの検証という「原点」を厳格に守り続けること。
それが、我々エンジニアが担うべき、終わりのないインシデントハンドリングの真髄である。コードを書く際、「これはブラウザにどう解釈されるか?」という問いを常に忘れないでほしい。その疑い深さこそが、最強の防壁になる。
コメント