【実務・中級編】URLスキーム(javascript:)のフィルタリングと検証 – アプリケーションセキュリティ & 安全な開発防御ガイド

なぜ「javascript:」スキームは、未だに開発者の盲点であり続けるのか

「href属性のバリデーションなんて、正規表現でhttpが含まれているか確認すればいいだろう?」

もし君がそう考えているなら、今すぐその甘い認識を捨ててくれ。現場のインシデント現場では、そんな「性善説に基づいたチェック」を通り抜けた攻撃コードが、連日のように管理者のセッションを奪い去っている。

クロスサイトスクリプティング(XSS)の防御において、最も初歩的でありながら、最も実装ミスが起きやすいのが「URLスキームのフィルタリング」だ。今回は、ただの「禁止リスト」運用がいかに無力か、そして「何を許容するか」を定義するホワイトリスト方式がいかに最強の防壁になるかを、実務レベルのコードと共に叩き込む。

—

攻撃者の視点:なぜ「javascript:」が恐ろしいのか

攻撃者は、ユーザーが入力したURLをそのままタグのhref属性に埋め込むような、隙だらけのアプリケーションを常にスキャンしている。

例えば、プロフィール編集画面でSNSのURLを入力させる機能を想像してほしい。ここで、攻撃者が入力するのはhttps://twitter.com/attackerではない。


SNSをチェック

このリンクを管理者や他のユーザーがクリックした瞬間、ブラウザはjavascript:に続くスクリプトをそのコンテキストで実行する。これが「反射型」や「格納型」のXSSだ。document.cookieを外部サーバーへ送信するスクリプトを仕込めば、認証セッションの乗っ取りは一瞬で終わる。

「でも、httpが含まれていないかチェックしているから大丈夫」?
残念ながら、javascript:alert('http://attacker.com') のように、悪意のあるスキームの中に正当なURLを混ぜる手法や、全角文字・タブ文字を混入させてブラックリストをすり抜ける手法は、攻撃者の基本手口だ。

—

防御の極意:ホワイトリスト方式による厳格な検証

セキュリティの本質は「拒否」ではなく「許可」にある。怪しいものを探すのではなく、安全だと断言できるもの以外すべてを遮断する。これがホワイトリスト戦略だ。

Pythonによる実装例:urllib.parseの活用

正規表現で頑張る必要はない。Pythonなら標準ライブラリを使って、「許可されたプロトコルか」を判定するのが最も堅牢だ。

from urllib.parse import urlparse

def is_safe_url(url):
“””
URLが許可されたスキーム(http/https/mailto)のみで構成されているか検証する
“””
allowed_schemes = {‘http’, ‘https’, ‘mailto’}
try:
parsed = urlparse(url)
# スキームが空の場合は相対パスとみなす(必要に応じて拒否するルールも検討せよ)
if not parsed.scheme:
return True
return parsed.scheme.lower() in allowed_schemes
except:
return False

テスト
print(is_safe_url(“javascript:alert(1)”)) # False
print(is_safe_url(“https://example.com”)) # True

JavaScript (Frontend) でのバリデーション

クライアントサイドでも必ずチェックを入れる。DOM操作を行う際のサニタイズには、DOMPurifyのような定評のあるライブラリを使うのが現代の鉄則だ。

// DOMPurifyを利用した安全なレンダリング
import DOMPurify from ‘dompurify’;

const userInput = “javascript:alert(‘XSS’)”;
const cleanHtml = DOMPurify.sanitize(リンク, {
ALLOWED_TAGS: [‘a’],
ALLOWED_ATTR: [‘href’]
});

// 結果: リンク (javascript:スキームは自動除去される)
document.getElementById(‘container’).innerHTML = cleanHtml;

—

インフラ層での防御:CSP (Content Security Policy) という最後の砦

アプリケーション層のコードが万が一汚染されても、インフラ側で被害を食い止めるのがプロの仕事だ。NginxなどのWebサーバーでContent-Security-Policyヘッダーを付与し、javascript:スキームの実行を根本から禁止する。

Nginx設定例:

信頼できないインラインスクリプトや、javascript:スキームを無効化する
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’; object-src ‘none’;”;

この設定を入れておけば、万が一コードのどこかにjavascript:スキームが紛れ込んでも、ブラウザが「ポリシー違反」として実行をブロックしてくれる。二重、三重の防御こそが、システムを堅牢にする唯一の道だ。

—

セキュリティチーフからの提言

君たちが書くコードは、単に動けばいいというものではない。そのコード一つひとつが、ユーザーの個人情報を守る盾になるべきだ。

1. ブラックリストは作るな: 未知の攻撃手法に一生追いかけ回される羽目になる。
2. ホワイトリストで縛れ: 「http/https/mailto」以外はすべて捨てる。それだけでリスクの9割は消える。
3. ライブラリを信じろ: 自作のサニタイズ関数は、たいていの場合、攻撃者にとっての「ザル」だ。DOMPurifyのような枯れたライブラリを正しく使え。

セキュリティ対応に「終わり」はないが、正しい設計思想を持てば、攻撃コストを劇的に引き上げることはできる。君たちが設計するシステムが、侵入困難な要塞となることを期待している。

何か実装で迷ったら、また相談に来い。コードレビューならいつでも歓迎する。

コメント

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