なぜ「window.○○ || {…}」が危険パターンなのか
一言で
この書き方は「window上に“それっぽい値”があれば何でも採用する」という意味になります。 すると攻撃者がDOM Clobberingなどで window.○○ を“偽物”で先に埋めれば、アプリはそれを正しい設定オブジェクトと信じて使ってしまいます。
もう少し具体的に(何が起きる?)
truthy/falsy だけで判定してしまう JavaScriptの
A || Bは、AがtruthyならAを返します。 つまりlet cfg = window.cfg || {avatar: '...'};は、window.cfg が「空でない・0でない・undefinedでない」なら何でも採用。 → 期待する型や中身を一切確認しない=型混入・データ混入が起こる。グローバル名前空間(window)は汚染されやすい 同じ id/name の要素を入れると、ブラウザは自動的に window.id名 を作る挙動があります(名前付き要素へのショートカット)。 → 攻撃者がコメントなどでDOMを注入すると、window.defaultAvatar などをHTMLCollectionや要素参照で上書きできる。 → その“偽物”が
||の左側でtruthyなので、右側の安全なデフォルトにフォールバックしない。ロード順の罠(先に汚染→後で採用) スクリプトが window.○○ || {...} を評価する時点で、すでにDOMや他スクリプトにより window.○○ が作られていたら、その値を無条件採用します。 → 時刻依存の不具合(TOCTOU)にもなり、再現が不安定でバグ化。
属性・URLシンクへの伝播でXSSに直結 採用した“偽物オブジェクト”のプロパティ(例:avatar)が、img.src など危険シンクに渡れば、 サニタイズの穴(例:cid:やクォート崩し)と組み合わせてonerror=…などを注入し、XSSになります。
意図しない“正しい値”の拒否・採用も起きる
正常値がたまたま falsy(空文字、0、false)だとデフォルトに置換される(想定外の挙動)。
- 逆に“危険な型”でも truthy なら無条件採用(型安全性ゼロ)。
例(悪い)と(より安全な)書き方
悪い例(問題のパターン)
// defaultAvatar が window に“あれば”それを、その中身の妥当性は見ない
let defaultAvatar = window.defaultAvatar || { avatar: '/resources/images/avatarDefault.svg' };
攻撃イメージ(DOM Clobberingで上書き)
<!-- ユーザーが挿入できる場所に、同じidを2つ --> <a id="defaultAvatar"></a> <a id="defaultAvatar" name="avatar" href="cid:"onerror=alert(1)//"></a>
ブラウザが window.defaultAvatar を HTMLCollection として生やし、 defaultAvatar.avatar.href が危険文字列に。アプリがこれを採用してXSSへ。
より安全なガード例(型・プロパティ・値の検証)
function isSafeUrl(u) {
try {
const url = new URL(u, location.origin);
// 許可するスキーム/ホスト/パスだけを明示
return url.protocol === 'https:' && url.hostname === location.hostname;
} catch { return false; }
}
function pickDefaultAvatar(win) {
const x = win.defaultAvatar;
if (x && typeof x === 'object' && !Array.isArray(x)) {
if (typeof x.avatar === 'string' && isSafeUrl(x.avatar)) {
return { avatar: x.avatar };
}
}
return { avatar: '/resources/images/avatarDefault.svg' };
}
const defaultAvatar = pickDefaultAvatar(window);
さらに理想は、グローバル(window)に依存しない設計(モジュールスコープや依存注入)です。
安全にするための指針(超要点)
- グローバル変数を信用しない(windowから読むなら型・スキーマ検証必須)。
- truthy/falsyだけで分岐しない(null合体演算子や明示条件+型ガードを使う)。
- DOM名→window汚染を前提にしない(要素はgetElementById等で明示取得)。
- 危険シンク手前でホワイトリスト検証(URLならスキーム・ホストを限定)。
- 不要スキームを禁止(サニタイザ設定で cid: などを閉じる)。
まとめ
「window.○○ || {…}」は、 (1) truthy/falsy だけの雑な採用、(2) window汚染(DOM Clobbering)との相性の悪さ、(3) ロード順依存 によって、攻撃者に偽オブジェクトを混入させやすい危険パターンです。 型・値・スキームを明示的に検証し、可能ならwindow依存をやめることが、安全な設計への近道です。
Best regards, (^^ゞ