Create Branch

XSS攻撃手法について

Published on 2025年1月11日

セキュリティ

XSSとは

攻撃者が不正なスクリプトを攻撃対象者が表示している画面に挿入して、ユーザーに不正なスクリプトを実行させる。

どのような種類があるか?

  • 反射型XSS
  • 蓄積型XSS
  • DOM-Based XSS(別記事でまとめる予定です。)

各XSSのついて

反射型XSS

特徴

特定のスクリプトの含まれたリンクにアクセスることにより、悪意のあるスクリプトが実行されます。
また、このタイプXSSは、アクセスごとにスクリプトが実行されるため状態であるため、限定的な範囲で被害に遭う。

実行フロー

説明

  • フェーズ1: 攻撃用URLの作成
    • 攻撃者が悪意のあるスクリプトを含むURLを作成
    • サンプルコード
    • http://example.com/search?q=悪意のあるコード
  • フェーズ2: 被害者への配布
    • 攻撃者が細工したURLをメールやSNSで被害者に送信
    • フィッシングなどのソーシャルエンジニアリングと組み合わせることが多い
  • フェーズ3: 攻撃の実行
    • 被害者が細工されたURLをクリック
    • Webアプリケーションがパラメータをそのまま反射(エコーバック)
    • ブラウザでスクリプトが実行される
  • フェーズ4: 情報の窃取
    • 実行されたスクリプトにより被害者の情報が攻撃者に送信される

蓄積型型XSS

特徴

フォームなどから投稿した不正なスクリプトを含むデータがDBに永続的に保存される。そのため、対策が取られるまで継続的に被害が発生する可能性があることを示しています。被害者がページにアクセスする度に攻撃が実行されるため、気付きにくいという特徴があります。

実行フロー

説明

  1. フェーズ1: 悪意のあるスクリプトの注入
    • 攻撃者がサービスにユーザー登録
    • 悪意のあるスクリプトを含むデータを投稿
  2. フェーズ2: 攻撃の実行
    • 被害者が投稿表示ページにアクセス
    • Webアプリケーションがデータベースからスクリプトを含むデータを取得
    • ブラウザでスクリプトが実行される
  3. フェーズ3: 情報の窃取
    • 実行されたスクリプトにより被害者の情報が攻撃者に送信される
    • 攻撃が継続的に行われる可能性がある
  4. 対策
    • フォームからデータを送信して保存する場合、必ず特殊文字を文字コードに置換しして保存するようにしましょう

実践

反射型XSSについて見てみる。
このリポジトリを公開しているのでクローンして見てローカルブラウザ実行して試すことも可能です。

GitHub

詳細

  1. 下記のようにリンククエリパラメーターに任意の値をに持たせるとブラウザ上に反映することが可能。※input要素にクエリ文字を反映できるように実装

  2. scriptの含まれたクエリ文字をブラウザで実行すると実行される

  3. imgのonerror属性で実行

    • 実行タイミング

      • 画像が呼び込まれない
      • エラーが発生した時にイベントが設定されていた時

対策

コードベースで詳細を記述しております。

// 1. Input要素での脆弱性と対策
// 脆弱性のある実装
function unsafeSetValue(keyword) {
    const input = document.getElementById('search');
    input.setAttribute('value', keyword); // 危険:直接値をセット
}

// 安全な実装
function safeSetValue(keyword) {
    const input = document.getElementById('search');
    // 特殊文字のエスケープ処理を行う
    const escaped = keyword.replace(/"/g, '"');
    input.setAttribute('value', escaped);
}

// 2. テキストノードを使用した安全な実装
function safeTextNode(content) {
    const div = document.getElementById('content');
    // テキストノードとして扱うことでstringとして扱われるため、HTML解析されないため、結果的に表示はHTMLタグを含んだ文字列が出力される。
    const textNode = document.createTextNode(content);
    div.appendChild(textNode);
}

// 3. aタグでの脆弱性と対策
// 脆弱性のある実装
function unsafeCreateLink(url) {
    const link = document.createElement('a');
    link.href = url; // 危険:直接URLをセット
    return link;
}

// 安全な実装
function safeCreateLink(url) {
    const link = document.createElement('a');
    // プロトコルのチェック(Content-Security-policyを用いることで同一オリジンのリソースに限定することで対応も可能です。)
    if (!/^https?:\/\//i.test(url)) {
        url = '#'; // 無効なURLの場合はデフォルト値
    }
    link.href = url;
    return link;
}

// 4. URLパラメータの取得と検証
function safeGetQueryParam(param) {
    const params = new URLSearchParams(window.location.search);
    const value = params.get(param);
    // 基本的なサニタイズ
    return value ? value.replace(/[<>'"]/g, '') : '';
}

// 5. エスケープ用ユーティリティ関数
const escapeHTML = (str) => {
    return str.replace(/[&<>"']/g, (match) => {
        const escape = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#39;'
        };
        return escape[match];
    });
};

// 使用例
document.addEventListener('DOMContentLoaded', () => {
    // パラメータの安全な取得
    const keyword = safeGetQueryParam('keyword');
    
    // 検索ボックスに安全にセット
    safeSetValue(keyword);
    
    // コンテンツの安全な表示
    safeTextNode(keyword);
    
    // リンクの安全な生成
    const link = safeCreateLink(keyword);
    document.body.appendChild(link);
});
hirotobeat