Webセキュリティ
Webセキュリティとは
Webアプリケーションを攻撃から守るための知識と実践の総称です。攻撃手法は数多くありますが、まず代表的な脆弱性のパターンを知ることが第一歩です。
脆弱性はどこから生まれるのか
Webの脆弱性の多くは、特殊な技術で作り込まれるわけではありません。大半は普通に動いているコードの副作用として生まれます。
- 投稿された文字をそのまま画面に表示する → 投稿にタグが混ざればブラウザは [[JavaScript]] として実行してしまう
- フォームの入力をそのまま SQL 文に連結する → 入力に
'が混ざれば SQL 文の区切り位置が変わる
どちらのコードも、機能としては注文どおりに動いています。攻撃者が狙うのは、受け取った値が行き着く先で特別な意味を持つ記号です。
ここで初学者がよくつまずくのが、「危険な文字を入口でまとめて弾けばよい」という発想です。たとえば <script という文字列を検出して拒否するフィルタは、<ScRiPt と大文字を混ぜられたり、<img onerror=...> のように別のタグを使われたりすれば簡単にすり抜けます。禁止したいパターンを列挙する方式は、いつまでも漏れが残ります。
正しい向きは逆です。出力する先の文法に合わせて無害化すること。同じ入力値でも、HTMLへ出すならエスケープ、SQLへ渡すならプレースホルダ、と処理は出力先ごとに変わります。入口のフィルタだけに頼った設計は、想定していなかった出力先が1つ増えた瞬間に崩れます。
代表的な脆弱性
| 脆弱性 | 攻撃の内容 | 基本の防御 |
|---|---|---|
| XSS | 悪意ある [[JavaScript]] をページに注入される | 出力時のエスケープ。Reactなどは自動でエスケープする |
| SQLインジェクション | 入力値で SQL を改ざんされる | プレースホルダ(パラメータ化クエリ)を必ず使う |
| CSRF | ログイン済みユーザーに意図しない操作をさせる | CSRFトークン、SameSite Cookie |
| セッションハイジャック | セッションIDを盗んでなりすます | [[HTTPS・TLS]] 必須、HttpOnly Cookie |
防御は層で重ねる
対策を1か所に集中させると、そこが漏れた瞬間に被害が最大化します。実務では次のように層を重ねます。
| 層 | 何をするか | 詳しく扱う記事 |
|---|---|---|
| アプリのコード | 出力先に合わせた無害化。ここが主対策 | [[SQLインジェクション]] |
| ブラウザへの指示 | 実行を許すスクリプトの出所を絞る | [[セキュリティヘッダー(CSP・HSTS)]] |
| 通信路 | 盗聴と改ざんを防ぐ | [[HTTPS・TLS]] |
| 通信の手前 | 既知の攻撃パターンを遮断する | [[WAF]] |
| 権限 | 破られたあとの被害範囲を狭める | [[最小権限の原則]] |
注意したいのは、下の層は上の層の代わりにならないという点です。WAF を入れたからエスケープを省いてよい、にはなりません。WAF は既知の攻撃パターンに一致する通信を弾く仕組みなので、形を少し変えた攻撃はすり抜けます。層を足す目的は主対策を省くことではなく、主対策が漏れたときに被害を止めることです。
次に読むための地図
Webセキュリティは範囲が広く、順番を決めずに読むと迷子になります。関心に近いところから入ってください。
ブラウザの中で起きること
[[XSS(クロスサイトスクリプティング)]] は他人のブラウザで任意のスクリプトを実行させる攻撃、[[CSRF]] はログイン中の利用者に意図しないリクエストを送らせる攻撃です。この2つは別物ですが、背景には「ブラウザが対応するサイトへ Cookie を自動で送る」という同じ性質があります。ブラウザはもともと「別オリジンのデータは読ませない」という同一オリジンポリシーで線を引いており、それをどこまで緩めるかを決めるのが [[CORS]] です。開発中に遭遇するあのエラーの意味もここで分かります。ただし CORS が制限するのはレスポンスの読み取り側なので、送信そのものを止める CSRF 対策にはなりません。
ログイン状態を守る
[[セッション管理]] は「ログイン中」をサーバーがどう覚えるかの基本形です。その札をブラウザのどこに置くかで安全性が変わるため、[[WebストレージとCookie]] の違い(Cookie だけがリクエストのたびに自動送信される)を先に押さえると理解が速くなります。何を確認し何を許可するかという設計そのものは [[認証と認可]] が担当します。
通信と身元の土台
[[HTTPS・TLS]] は盗聴・改ざんを防ぐ前提条件です。そのうえで「つないだ相手が本物か」を保証しているのが [[電子証明書とPKI]] で、ブラウザの鍵アイコンが何を保証しているのかが分かると、証明書の警告画面を安易に無視できなくなります。ネットワークの境界でポート単位の出入りを制御するのは [[ファイアウォール]] の役割で、HTTP の中身まで検査する [[WAF]] とは守備範囲が異なります。
作り込まない・見つける・直す
脆弱性は世に出る前に止めるのが最も安く済みます。観点を決めた [[コードレビュー]] が効くのはそのためで、[[AIコーディング支援]] が生成したコードも当然レビュー対象です。すでに公表されている弱点を追い続ける活動が [[脆弱性管理とCVE]]、実際に侵入できるかを許可の範囲で実証するのが [[ペネトレーションテスト]] です。
初学者向けポイント
- 共通原則は「外部からの入力をすべて信用しない」。フォーム、URL、ヘッダー、すべてが攻撃の入り口になりうる
- フレームワークの標準機能(自動エスケープ、ORM)を素直に使うのが最善の防御
- 秘密情報(APIキー等)をフロントエンドのコードや Git リポジトリに含めない
学び方
IPA「安全なウェブサイトの作り方」とOWASP Top 10 が定番の教材です。OWASP Top 10 は重大なリスクを10項目に絞ったリストで、数年ごとに改訂されます。攻撃の仕組みを理解すると防御の意味が腹落ちします。
関連技術とのつながり
- [[認証と認可]] — 攻撃者が最も狙う機能
- [[HTTPS・TLS]] — 通信路の防御。すべての前提
- [[SQLインジェクション]] — 入力値でSQLを改ざんされる攻撃。原理と防御をここで詳しく扱う
- [[JavaScript]] — XSSの理解に必要
- [[シークレット管理]] — APIキーなどの秘密情報を安全に保管・配布する具体策
- [[セキュリティヘッダー(CSP・HSTS)]] — ブラウザ側に防御を指示する多層防御の層
- [[プロンプトインジェクション]] — 「入力を信用しない」原則をLLMへの入力に広げた攻撃
Q: 同じ入力値でも、HTMLへ出すときとSQLへ渡すときで無害化の方法が違うのはなぜ?
- [x] 値が行き着く先ごとに、特別な意味を持つ記号が違うから
- [ ] HTMLよりSQLのほうが処理速度が速いから
- [ ] ブラウザが入力値を自動で暗号化するから
解説: 攻撃者が狙うのは出力先の文法で意味を持つ記号です。HTMLならエスケープ、SQLならプレースホルダというように、無害化は出力先ごとに変わります。
Q: 入口で `<script` という文字列を拒否するフィルタの問題点はどれ?
- [ ] 正しい入力まで暗号化してしまう
- [x] 大文字小文字を混ぜたり別のタグを使ったりすれば迂回される
- [ ] サーバーの通信が暗号化されなくなる
解説: 禁止パターンを列挙する方式は漏れが残ります。`<ScRiPt` や `<img onerror=...>` のような形で簡単にすり抜けます。
Q: WAFを導入した場合、アプリ側のエスケープやプレースホルダはどう扱うべき?
- [ ] WAFが遮断するので省いてよい
- [x] 主対策として引き続き必要。WAFは漏れたときに被害を止める層
- [ ] WAFと同時に使うと誤検知が増えるので外すべき
解説: WAFは既知の攻撃パターンを弾く仕組みで、形を変えた攻撃はすり抜けます。下の層は上の層の代わりにならず、主対策はアプリのコード側にあります。