Webアプリケーションの脆弱性は、特別な知識がなくても、基本的な書き方のミスから生まれます。
新人研修やセキュリティ教育でも、最初に学ぶのが、XSS、CSRF、SQLインジェクションの3つです。
この記事では、それぞれの仕組みと、開発者が行うべき基本的な対策を整理します。
1. XSS(クロスサイトスクリプティング)
どんな攻撃か
ユーザーが入力した値を、そのままHTMLに出力してしまうと、攻撃者が仕込んだJavaScriptが、他の利用者のブラウザ上で実行されてしまいます。
たとえば掲示板のコメント欄に、スクリプトを含む文字列が投稿され、それを閲覧した人のCookieやセッション情報が盗まれる、といった被害につながります。
対策
- 出力時のエスケープ:
<>&"などをHTMLエンティティに変換して出力する。
Jinja2などの多くのテンプレートエンジンは、既定で自動エスケープを行う。 - エスケープを無効にしない:Jinja2の
|safeなど、エスケープを解除する機能は、信頼できる値にだけ使う。 - Cookieに HttpOnly 属性を付ける:JavaScriptからセッションCookieを読めないようにする。
- Content-Security-Policy(CSP)の導入:読み込めるスクリプトの出どころを制限し、被害を軽減する。
2. CSRF(クロスサイトリクエストフォージェリ)
どんな攻撃か
ログイン中の利用者に、攻撃者が用意した罠のページを開かせ、利用者の意図しないリクエスト(パスワード変更、送金、投稿など)を、本人の権限で送信させる攻撃です。
ブラウザが、Cookieを自動で送ってしまう性質を悪用します。
対策
- CSRFトークン:フォームごとに予測できないトークンを埋め込み、サーバー側で検証する。
- SameSite属性:セッションCookieに
SameSite=Lax(またはStrict)を設定し、外部サイトからのリクエストにCookieを付けない。 - 状態を変更する操作にGETを使わない:更新や削除は、POST・PUT・DELETEなどで行う。
- 重要な操作では再認証:パスワード変更などで、現在のパスワードを再度確認する。
3. SQLインジェクション
どんな攻撃か
ユーザーの入力を、SQL文に文字列として連結すると、攻撃者がSQLの一部を書き換えられます。
ログインの認証回避、データの盗み見・改ざん・削除といった、深刻な被害につながります。
悪い例と良い例
# 悪い例:文字列を連結している
cur.execute("SELECT * FROM users WHERE name = '" + name + "'")
# 良い例:プレースホルダ(バインド変数)を使う
cur.execute("SELECT * FROM users WHERE name = %s", (name,))
対策
- プレースホルダ(バインド変数)を使う:SQLの構造と値を分離する。
最も重要な対策。 - ORMを使う:多くのORMは、既定でプレースホルダを使う。
ただし、生のSQLを組み立てる機能では、同じ注意が必要。 - データベースの権限を最小限にする:アプリケーション用のアカウントに、不要な権限(テーブルの削除など)を与えない。
- 詳細なエラーを利用者に見せない:SQLの内容やテーブル名が、攻撃の手がかりになる。
共通する考え方
- 入力は信用しない:フォーム、URL、ヘッダー、Cookie、外部APIの応答など、外部から来る値はすべて、不正な値が来る前提で扱う。
- 検証は入力時、無害化は出力時:入力値の形式チェックと、出力先に応じたエスケープは、別の対策として両方行う。
- フレームワークの機能を使う:自前で対策を書くより、実績のあるフレームワークやライブラリの機能に任せるほうが安全。
- 依存ライブラリを更新する:既知の脆弱性が修正された最新版を使い続ける。
- HTTPSを使う:通信の盗聴・改ざんを防ぐ。
さらに学ぶには
Webアプリの代表的なリスクを体系的にまとめた資料として、OWASP(Open Worldwide Application Security Project)の「OWASP Top 10」があります。
また、IPA(情報処理推進機構)の「安全なウェブサイトの作り方」は、日本語で読める実践的な資料です。
開発前や、研修の教材として、ぜひ参考にしてください。
まとめ
XSS、CSRF、SQLインジェクションは、いずれも基本的な対策を守ることで、大部分を防げます。
「エスケープ」「トークン・SameSite」「プレースホルダ」という3つの基本を、開発の習慣にしておきましょう。