Webサイトで起こる3つの代表的な攻撃を学ぶ

Webサイトで起こる3つの代表的な攻撃を学ぶ

とちとち
公開日:2026/09/28
読了目安:約15分

Webサイトで起こる3つの代表的な攻撃

SQLインジェクション・XSS・CSRFを仕組みから理解する

Webサイトには、利用者が入力した値、画面へ表示する内容、ブラウザから届くリクエストなど、外部から受け取る情報が数多くあります。これらを無条件に信用すると、本来はデータだったものが「命令」として解釈されたり、本人が意図していない操作が実行されたりします。

この記事では、代表的な攻撃であるSQLインジェクション、XSS、CSRFを取り上げます。攻撃用の具体的な文字列は扱いません。初心者でも違いをつかめるように、データがどこから入り、どこで意味を持ち、どこを守るのかという順番で説明します。

入力、表示、リクエストという3つの入口を示した表紙

3つの攻撃は、狙われる場所が違う

Webサイトでは、利用者のブラウザからWebサーバーへリクエストが送られます。Webサーバーは必要に応じてデータベースを読み書きし、処理結果をブラウザへ返します。

ブラウザ、Webサーバー、データベースの流れと、3つの攻撃が狙う場所

3つの攻撃は名前も仕組みも異なりますが、データの流れに沿って見ると整理しやすくなります。

  • SQLインジェクションは、入力値がデータベースへの命令に混ざる問題
  • XSSは、表示した内容が利用者のブラウザで命令として動く問題
  • CSRFは、ログイン中の利用者に意図しない操作をさせる問題

共通しているのは、外部から来たものを正しい文脈で扱う必要があることです。入力値そのものに善悪が書かれているわけではありません。どの場所で、どのような意味として解釈されるかが問題になります。

1. SQLインジェクション:入力値がデータベースへの命令になる

検索画面やログイン画面では、利用者が入力した文字を使ってデータベースを検索します。このとき、SQL文と入力値を文字列として直接つないでしまうと、入力値の一部がSQLの命令として解釈される場合があります。これがSQLインジェクションです。

SQL文と入力値を直接つなぐ危険な流れと、分離して扱う安全な流れ

危険なのは「文字列を受け取ること」ではなく「SQLへ混ぜること」

例えば、次のように入力値をSQL文へ直接つなぐ作り方は危険です。

sql = "SELECT ... WHERE email = '" + 入力値 + "'"

この形では、プログラムが用意したSQLと、利用者が送った値の境界が曖昧です。入力内容によっては、データベースが全体を1つの命令として解釈してしまいます。

一方、パラメータ付きクエリでは、SQLの構造と値を分けて渡します。

sql = "SELECT ... WHERE email = ?" db.query(sql, 入力値)

データベースは「?」に渡された内容を検索値として扱います。入力にSQLのような文字が含まれていても、命令の構造を変えにくくなります。

成立すると何が起きるのか

SQLインジェクションが成立した場合、影響は検索結果の変化だけにとどまりません。アプリケーションに与えられたデータベース権限によっては、次のような被害へ広がります。

  • 本来は見られない情報を取得される
  • 利用者情報や設定を書き換えられる
  • データを削除される
  • 認証処理を回避される

データベースの利用者に強すぎる権限が与えられていると、被害も大きくなります。

基本対策と補助対策を分けて考える

中心となる対策は、 プリペアドステートメントやパラメータ付きクエリを使い、SQLと値を分離すること です。入力値のエスケープだけで守ろうとすると、データベースや文字コードの違い、実装漏れなどで破られるおそれがあります。

入力値の形式チェックも必要です。年齢なら数値、識別子なら決められた形式というように、仕様に合わない値を早い段階で拒否します。ただし、形式チェックはパラメータ付きクエリの代わりにはなりません。

テーブル名や並び順など、パラメータとして渡せない部分を動的に切り替える場合は、プログラム側で許可した候補から選びます。さらに、データベースの権限を必要最小限にし、万一のときに被害が広がりにくい構成にします。

レビューで確認すること

  • SQL文を文字列連結で組み立てていないか
  • ORMやデータベースライブラリの安全なAPIを使っているか
  • 動的なテーブル名や並び順を許可リストで制限しているか
  • アプリケーションのDB権限が必要以上に強くないか
  • エラー画面にSQL文やテーブル名を表示していないか

2. XSS:表示した内容がブラウザで実行される

掲示板、プロフィール、検索結果などでは、利用者が入力した内容を画面へ表示します。このとき、文字として表示するはずの内容がHTMLやJavaScriptとして解釈されると、別の利用者のブラウザで悪意のある処理が動く可能性があります。これがXSS(クロスサイト・スクリプティング)です。

投稿された文字列がWebサイトを経由し、エスケープの有無で実行または文字表示に分かれる流れ

XSSには複数の入り方がある

XSSは、悪意のある内容がどこを通るかによって整理できます。

保存型XSS では、投稿内容やプロフィールなどに悪意のある文字列が保存されます。その画面を開いた別の利用者にも影響するため、被害が広がりやすい種類です。

反射型XSS では、URLの検索条件やエラー内容など、リクエストに含まれた値がすぐ画面へ返されます。細工されたURLを開いた利用者のブラウザで問題が起こります。

DOMベースXSS では、ブラウザ上のJavaScriptがURLや入力値を読み取り、危険な方法で画面を書き換えます。サーバー側のHTMLに問題が見当たらなくても、ブラウザ内の処理だけで成立する場合があります。

3つを暗記するより、「外部から来た値が、ブラウザでコードとして解釈される場所へ入っていないか」と考える方が実装時には役立ちます。

成立すると何が起きるのか

XSSが成立すると、攻撃者の処理が正規サイトの画面内で動きます。そのため、利用者からは本物の画面との区別がつきにくくなります。

  • 偽のログイン画面や入力欄を表示される
  • 画面上の情報や利用者の操作を読み取られる
  • 利用者の権限でWebサイトの機能を操作される
  • 不正なページへ誘導される

Cookieに「HttpOnly」を設定すると、JavaScriptからCookieを直接読み取りにくくできます。ただし、XSSそのものを防ぐ設定ではありません。悪意のある処理が正規画面上で動けば、Cookieを盗めなくても利用者の権限で操作できる場合があります。

表示する場所に合った処理が必要

基本は、値を画面へ出す直前に、 表示先の文脈に合わせてエスケープすること です。ブラウザは、HTML本文や属性、URL、JavaScript、CSSを異なる規則で解釈します。したがって、同じエスケープ処理をどこでも使えばよいわけではありません。

多くのフレームワークには自動エスケープがあります。まずは既定の安全な表示方法を使い、未検証の値を「innerHTML」などへ直接渡さないようにします。単なる文字を表示する場合は、ブラウザが文字として扱う「textContent」のような安全な方法を選びます。

記事投稿などで利用者に一部のHTMLを許可する場合は、すべてを文字へ変換すると機能が成立しません。その場合は、危険な要素や属性を取り除くHTMLサニタイズを行います。

Content Security Policy(CSP)は、読み込めるスクリプトなどを制限する追加の防御です。実装ミスの影響を抑える助けになりますが、出力時のエスケープやサニタイズの代わりにはなりません。

レビューで確認すること

  • フレームワークの自動エスケープを無効にしていないか
  • 「innerHTML」など、文字列をHTMLとして解釈する処理へ値を渡していないか
  • URLやHTML属性など、出力先に合った処理をしているか
  • HTMLを許可する箇所で、実績のあるサニタイズ処理を使っているか
  • CSPやCookie属性だけでXSS対策を完了したことにしていないか

3. CSRF:ログイン中の利用者に意図しない操作をさせる

CSRF(クロスサイト・リクエスト・フォージェリ)は、攻撃者が利用者のパスワードを盗んでログインする攻撃とは異なります。すでにログインしている利用者のブラウザを利用し、本人が意図していないリクエストを正規サイトへ送らせます。

ログイン中のブラウザがCookie付きリクエストを自動送信する流れと、トークンや送信元の検証で拒否する流れ

ブラウザがCookieを自動送信する性質を利用する

利用者がWebサイトへログインすると、ブラウザにはセッションを識別するCookieが保存されます。対象サイトへのリクエストでは、ブラウザがCookieを自動的に付ける場合があります。

この状態で利用者が攻撃者の用意した別サイトを開くと、そのページから正規サイトへリクエストを送らされることがあります。正規サイトがCookieだけを見て本人の操作だと判断すると、意図しない処理が実行されます。

CSRFが成立するには、主に次の条件が重なります。

  1. 利用者が対象サイトへログインしている
  2. ブラウザが認証用Cookieを自動的に付ける
  3. データを変更する処理が外部サイトから送信できる
  4. サーバーが操作の出所やトークンを確認していない

成立すると何が起きるのか

被害は、ログイン中の利用者に許可されている操作の範囲で起こります。

  • メールアドレスや通知設定を変更される
  • データを登録・更新・削除される
  • 意図しない申込みや送信処理を実行される
  • 管理者が狙われた場合、管理機能を操作される

複数の確認を組み合わせる

代表的な対策は、サーバーが発行した CSRFトークン を画面やリクエストへ組み込み、更新処理の前に一致を確認することです。攻撃者のサイトは正しいトークンを取得できないため、不正なリクエストを拒否できます。

Cookieの「SameSite」属性は、外部サイトからのリクエストにCookieを付ける条件を制限します。ただし、同じ登録ドメイン配下の別サブドメインをどこまで信用するか、GETで状態変更をしていないかなど、構成によって注意点があります。多くのシステムでは、CSRFトークンや送信元確認と組み合わせる追加の防御として扱います。

サーバー側で「Origin」や「Referer」を確認し、想定した送信元から来たリクエストか検証する方法もあります。さらに、対応ブラウザでは「Sec-Fetch-Site」などのFetch Metadataを補助信号として利用できます。

データを変更する処理にはGETを使いません。POSTへ変えるだけでCSRF対策が完了するわけではありませんが、リンクを開いただけで状態が変わる設計は避ける必要があります。

CORSとは役割が異なる

CORSは、別オリジンのJavaScriptがレスポンスを読み取れる範囲を制御する仕組みです。一方、CSRFではレスポンスを読めなくても、更新リクエストが送信されて処理されれば被害が発生します。

そのため、「CORSを設定したからCSRFも防げる」とは限りません。操作の正当性は、CSRFトークンや送信元確認などで別に検証します。

レビューで確認すること

  • 更新・削除処理にCSRF対策が適用されているか
  • GETリクエストでデータを変更していないか
  • Cookieの「SameSite」「Secure」「HttpOnly」を用途に合わせて設定しているか
  • 「Origin」や「Referer」の検証方針が決まっているか
  • CORSやPOSTへの変更だけで対策済みとしていないか

3つの違いは「どこで意味が変わるか」で見分ける

SQLインジェクション、XSS、CSRFについて、狙われる場所と確認事項を比較した図
攻撃狙われる場所問題になる境界中心となる対策
SQLインジェクションデータベース値がSQLの命令になるパラメータ付きクエリ
XSS利用者のブラウザ値が画面上のコードになる文脈に合ったエスケープ
CSRFWebサイトの更新処理外部からの送信が本人の操作になるCSRFトークンと送信元確認

SQLインジェクションとXSSは、データが命令として解釈される点が似ています。ただし、命令を実行するのは前者がデータベース、後者がブラウザです。CSRFは入力値の解釈よりも、リクエストを送ったのが誰の意図なのかが問題になります。

セキュリティ対策は境界ごとに重ねる

単独の対策ですべては防げません。入力形式を確認し、データベースへ渡すときはSQLと値を分け、画面へ出すときは文字として扱い、更新処理では操作の出所を確認します。

入力、データベース、表示、リクエストの各境界で行う対策

この考え方は「多層防御」と呼ばれます。どれか1つが漏れた場合も、別の層で被害を防いだり小さくしたりできます。

ただし、補助対策を入れたことで根本対策が不要になるわけではありません。WAFが不審な通信を遮断しても、文字列連結でSQLを組み立てる実装は残ります。CSPを設定しても、危険な出力処理は修正が必要です。

よくある誤解

入力チェック、POST、WAFやCSPに関する3つの誤解

「入力チェックをすれば、すべて防げる」

入力チェックは、仕様外の値を早く拒否するために有効です。しかし、自由入力には記号や改行が正当なデータとして含まれることもあります。入力時に危険そうな文字をすべて削除すると、機能を壊したり、別の経路から来たデータを見落としたりします。

入力チェックに加えて、データがSQLになる場所、HTMLになる場所、更新リクエストとして処理される場所で、それぞれに合った対策を行います。

「POSTならCSRFは起きない」

別サイトからPOSTリクエストを送信させる方法はあります。POSTは「データを変更する処理で使うHTTPメソッド」であり、それだけで送信者の正当性を証明するものではありません。

「WAFやCSPがあれば実装側は直さなくてよい」

WAFやCSPは重要な防御層ですが、通信やブラウザの動作を制限する補助対策です。アプリケーションが値と命令を混ぜている場合は、その実装を修正する必要があります。

実装とレビューで使えるチェックリスト

設計時

  • どの画面やAPIで外部から値を受け取るか
  • 受け取った値が、DB・HTML・URL・JavaScriptのどこへ渡るか
  • データを変更する処理はどれか
  • 利用者、一般管理者、システム管理者で権限が分かれているか

実装時

  • SQLはパラメータ付きクエリで実行する
  • 出力時はフレームワークの安全なAPIを優先する
  • HTMLを許可するときはサニタイズする
  • 更新処理ではCSRFトークンや送信元を検証する
  • Cookieに用途に合った属性を設定する
  • エラーへ内部情報や入力内容を出しすぎない

テスト時

  • 通常の値だけでなく、空文字、長い文字列、記号を含む値も確認する
  • 権限のない利用者が更新APIを実行できないことを確認する
  • CSRFトークンがない、または一致しないリクエストが拒否されることを確認する
  • 投稿内容がHTMLとして実行されず、文字として表示されることを確認する
  • 静的解析や依存ライブラリの脆弱性検査も併用する

実装時には、次の3点を問い直します。

  1. 入力値を命令と分けて扱っているか
  2. 表示内容を、画面上のコードとして実行させていないか
  3. リクエストが、本当に利用者の意図した操作か確認しているか

入力値・表示内容・リクエストを、そのまま信用しない。

この視点があれば、3つの攻撃を別々の暗記項目で終わらせず、SQLへ渡す直前、HTMLへ差し込む直前、更新APIを実行する直前という具体的な確認地点に置き換えられます。

用語

  • ブラウザ :Webサイトを表示し、サーバーと通信するアプリケーション
  • Webサーバー :ブラウザからリクエストを受け取り、処理結果を返すサーバー
  • データベース :利用者情報やサービスのデータを保存・検索する仕組み
  • Cookie :ログイン状態などをブラウザ側で保持するための情報
  • エスケープ :特別な意味を持つ文字を、単なる文字として扱える形へ変換する処理
  • サニタイズ :HTMLなどから、危険な要素や属性を取り除く処理
  • オリジン :通信先を「スキーム・ホスト・ポート」の組み合わせで区別する考え方
  • 許可リスト :利用を認める値や形式をあらかじめ決め、それ以外を拒否する方式

参考資料