- Webアプリケーションで起こりやすい代表的な脆弱性を知る
- SQLインジェクション・XSS・CSRFの「何が危ないのか」を説明できる
- それぞれの基本的な対策を、実装やレビュー時に意識できるようにする
- 「入力値を信頼しない」「出力時に正しく扱う」「重要操作を守る」という基本姿勢を身につける
Webセキュリティでまず意識すること
Webアプリケーションは、利用者の入力を受け取り、サーバやデータベースで処理し、結果をブラウザに返します。
便利な一方で、入力値やリクエストの扱いを誤ると、攻撃者に悪用されることがあります。
まず次の3つを意識しましょう。
- 入力値をそのまま信用しない
- 画面に表示するときは、文字列として安全に扱う
- 重要な操作は、本人の意図した操作か確認する
攻撃手法の名前を丸暗記するよりも、「どこで信頼してはいけないものを信頼してしまったのか」を考えると理解しやすくなります。
Webにおける代表的な脆弱性
このページでは、Webアプリケーションで特に有名な脆弱性を扱います。
- HTMLソースからの情報漏洩
- SQLインジェクション
- XSS(クロスサイトスクリプティング)
- CSRF(クロスサイトリクエストフォージェリ)
どれも、実装や設定の小さなミスから大きな事故につながる可能性があります。
それぞれの脆弱性について、次の順番で理解しましょう。
- 何が起きるのか
- なぜ起きるのか
- どんな被害につながるのか
- どう防ぐのか
HTMLソースからの情報漏洩
HTMLソースは、利用者のブラウザから確認できます。
そのため、HTML内に不要なコメントや内部情報を残すと、外部に見えてしまう可能性があります。
例えば、次のような情報がHTMLソースに含まれていると危険です。
- 開発者名や社内向けコメント
- テスト用のURL
- 管理画面へのパス
- APIキーや認証情報
- バージョン管理用のコメント
実際に、コメントアウトを残したまま本番環境にデプロイし、開発者名などが外部から見えてしまう事例もあります。
対策
- 本番環境に不要なコメントやデバッグ情報を残さない
- APIキー・パスワード・トークンなどの秘密情報をHTMLやJavaScriptに埋め込まない
- レビュー時に「ブラウザから見える情報」を確認する
- ビルドやデプロイの仕組みで、不要な情報を除去する
「コメントアウトしているから安全」ではありません。HTMLやJavaScriptに含まれる情報は、利用者から見える前提で考えましょう。
SQLインジェクション
SQLインジェクションとは、入力値に悪意のあるSQLの一部を混ぜ込み、データベースへの命令を攻撃者の意図した形に変えてしまう攻撃です。
例えば、ログイン画面や検索フォームなど、ユーザー入力をもとにSQLを作る処理で発生します。
通常時
通常は、入力された値を条件としてデータベースを検索します。
脆弱性を利用されると
入力値がSQL文の一部として扱われてしまうと、攻撃者が本来意図しない条件を追加できてしまいます。
その結果、次のような被害につながる可能性があります。
- 認証を回避される
- 本来見えてはいけないデータを取得される
- データを書き換えられる
- データを削除される
条件が常に正しいものとなってしまう。
SQLインジェクションが起きる原因
主な原因は、ユーザー入力をSQL文字列に直接連結してしまうことです。
悪い例のイメージ:
SELECT * FROM users WHERE name = '入力値';
ここで入力値にSQLとして意味を持つ文字列が入ると、命令の意味が変わってしまいます。
ユーザー入力は、画面のフォームだけではありません。URLパラメータ、HTTPヘッダ、Cookie、外部APIから受け取る値なども、外部から来る入力として扱います。
SQLインジェクションの対策
プリペアドステートメントを利用する
プリペアドステートメント(静的プレースホルダ)を使うと、SQL文と入力値を分けて扱えます。
これにより、入力値がSQL命令として解釈されにくくなります。
WebフレームワークやORMを利用する
DjangoなどのWebフレームワークでは、ORM(Object-Relational Mapping) が提供されています。
ORMを使うと、SQLを直接文字列として組み立てる場面を減らせます。
# Djangoの例
user = User.objects.get(username=user_input)
ただし、フレームワークを使えば必ず安全というわけではありません。
生SQLを使う場合や、特殊なクエリを書く場合は、プリペアドステートメントなどを適切に使う必要があります。
WAFを利用する
WAF(Web Application Firewall) は、Webアプリケーションへの不正な通信を検知・遮断する仕組みです。
SQLインジェクションのような攻撃パターンを検知できる場合があります。
攻撃を識別するためのルールを シグネチャ と呼びます。
ただし、WAFは補助的な対策です。アプリケーション側の実装を安全にすることが基本です。
SQLを文字列連結で組み立てない。入力値は「命令」ではなく「値」として扱う。
XSS(Cross Site Scripting)
XSSは、Webページ上で攻撃者が用意した悪意のあるスクリプトを実行させる攻撃です。
CSS(Cascading Style Sheets)と区別するため、Cross Site Scriptingは XSS と略されます。
XSSは、HTMLを動的に生成するときに、ユーザー入力やリクエストデータをそのままHTMLとして表示してしまうことで発生します。
XSSで起こりうる被害
- セッションIDやCookieの不正取得
- 利用者になりすました操作
- 画面表示の改ざん
- 偽の入力フォーム表示
- 不正サイトへの誘導
つまり、攻撃者が利用者のブラウザ上でスクリプトを動かせることが問題です。
XSSの種類
反射型(リフレクト)XSS
URLなどに含まれた悪意のあるスクリプトが、アクセスしたページ上でそのまま表示・実行されるタイプです。
通常の検索URLの例:
https://example.com/search?word=ニフティ
攻撃者が送るURLの例:
https://example.com/search?word=<script>alert('XSS Attack!')</script>
利用者がこのURLをクリックすると、脆弱性のあるサイト上でスクリプトが実行される可能性があります。
蓄積型(格納型、Stored)XSS
掲示板、コメント欄、プロフィール欄など、Webサイトに保存されるデータの中に悪意のあるスクリプトを混ぜ込むタイプです。
保存されたスクリプトは、そのページを見た他の利用者のブラウザで実行される可能性があります。
<script>alert('あなたのクッキー情報: ' + document.cookie);</script>
一度保存されると複数の利用者に影響するため、特に被害が広がりやすいタイプです。
DOM-based XSS
サーバ側ではなく、ブラウザ上のJavaScriptがHTMLを動的に書き換える処理で発生するタイプです。
例えば、URLの一部を取得して、そのままHTMLとして画面に埋め込むような処理があると危険です。
// 攻撃者がURLフラグメントにスクリプトを含める
http://example.com/#<script>alert('XSS Attack!');</script>
// 被害者がそのURLを開いた際に実行されるJavaScript
var hash = window.location.hash;
document.body.innerHTML = hash; XSSは「入力された文字列を、文字列ではなくHTMLやJavaScriptとして扱ってしまう」ことで起こります。
XSSの対策
出力時にエスケープする
Webページに表示する値は、HTMLとして解釈されないようにエスケープします。
| 文字 | 変換後の例 |
|---|---|
| ' | ' |
| " | " |
| < | &lt; |
| > | &gt; |
| & | &amp; |
属性値を引用符で囲む
HTML属性に値を入れる場合は、値を引用符で囲みます。
引用符で囲まない場合、攻撃者がイベント属性などを追加できてしまう可能性があります。
<input type="button" value=$data>
<input type="button" value=1 onclick=悪意のあるスクリプト>
引用符で囲むことで、入力値全体を属性値として扱いやすくなります。
<input type="button" value="$data">
<input type="button" value="1 onclick=悪意のあるスクリプト">
ただし、引用符で囲むだけで十分ではありません。属性値に入れる場合も、文脈に応じたエスケープが必要です。
URLのスキームを確認する
リンク先URLをユーザー入力から作る場合、javascript: のような危険なスキームを許可しないようにします。
if ($data !~ /^(http|https):/) {
// エラー
} CSPを導入する
CSP(Content Security Policy) は、ブラウザに対して「どこから読み込んだスクリプトを実行してよいか」などを指示する仕組みです。
HTTPヘッダやmetaタグで指定します。
Content-Security-Policy: default-src 'self' <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
CSPにより、インラインスクリプトや不審な外部スクリプトの実行を制限できます。
CookieにHttpOnly属性やSecure属性を付与する
Cookieを守るために、次の属性を利用します。
- HttpOnly属性:JavaScriptからCookieを読み出せないようにする
- Secure属性:HTTPS接続時のみCookieを送信する
これにより、XSSが発生した場合でもCookieが盗まれるリスクを下げられます。
Webフレームワークを利用する
既存のフレームワークやライブラリを利用すると、XSS対策が組み込まれたテンプレート機能を使える場合があります。
例:
- FlaskやDjangoは、テンプレートで自動エスケープを提供している
ただし、自動エスケープを無効化したり、HTMLを直接埋め込んだりする場合は注意が必要です。
入力値を画面に表示するときは、「どこに出力するのか」に応じて適切にエスケープする。必要に応じてCSPやCookie属性も組み合わせる。
CSRF(Cross Site Request Forgery)
CSRFは、攻撃者が利用者のブラウザを悪用し、利用者が意図していないリクエストを送信させる攻撃です。
利用者がターゲットサイトにログイン済みの場合、ブラウザには有効なセッション情報が残っています。
攻撃者はそれを利用して、利用者本人が操作したように見えるリクエストを送らせます。
攻撃が成功すると、次のような被害が起こる可能性があります。
- 意図しない設定変更
- 意図しない投稿
- 意図しない送金や購入
- メールアドレスやパスワードの変更
CSRFの流れ
- ターゲットサイトにログインする
利用者が自分の認証情報でWebサイトにログインし、セッションが有効になります。 - 悪意のあるサイトを訪問する
そのサイトには、ターゲットサイトへリクエストを送る仕組みが仕込まれています。 - 意図しないリクエストが送信される
利用者のブラウザを通じて、ターゲットサイトに不正なリクエストが送られます。 - ターゲットサイトが正当な操作として処理してしまう
セッションが有効なため、利用者本人の操作として扱われる可能性があります。
IPAより引用(https://www.ipa.go.jp/security/vuln/websecurity/csrf.html)
XSSは「利用者のブラウザ上で悪意のあるスクリプトを実行させる攻撃」です。CSRFは「利用者のログイン状態を悪用して、意図しないリクエストを送らせる攻撃」です。
CSRFの対策
CSRFトークンを使用する
サーバ側で CSRFトークン と呼ばれる推測困難な値を生成し、フォームに埋め込みます。
リクエスト時にトークンを検証することで、正規の画面から送られたリクエストかどうかを確認できます。
<form method="POST" action="post.cgi">
<input type="hidden" name="csrf_token" value="550e8400e29b41d4a716446655440000">
<input type="text" name="msg">
</form> CookieのSameSite属性を使用する
Cookieの SameSite属性 を Strict または Lax に設定すると、クロスサイトリクエスト時にCookieが送信されにくくなります。
これにより、ログイン状態を悪用したリクエストを防ぎやすくなります。
重要な操作ではユーザーインタラクションを要求する
重要な操作の前に、利用者へ追加確認を求めます。
- 確認画面を表示する
- パスワード再入力を求める
- 多要素認証を求める
特に、送金・退会・権限変更・メールアドレス変更などの重要操作では有効です。
「ログイン済みだから本人の意図した操作」とは限りません。重要なリクエストでは、正規の画面から送られたものかを確認します。
まとめ
| 脆弱性 | 何が起きるか | 基本対策 |
|---|---|---|
| HTMLソースからの情報漏洩 | ブラウザから見える場所に内部情報が出てしまう | 不要なコメント・秘密情報を含めない |
| SQLインジェクション | 入力値によってSQLの意味が変わってしまう | プリペアドステートメント、ORM、WAF |
| XSS | 利用者のブラウザで悪意のあるスクリプトが実行される | 出力時のエスケープ、CSP、Cookie属性 |
| CSRF | 利用者のログイン状態を悪用して意図しない操作をさせる | CSRFトークン、SameSite属性、重要操作の再確認 |
- 外部から来る値をそのまま信用しない
- SQL・HTML・URLなど、使う場所に応じて安全に扱う
- フレームワークの機能を正しく使う
- 「便利だから」「動くから」で危険な実装をしない
- 不安な実装は、早めに相談・レビューする