Webセキュリティ対策

更新日:

Webサイトは世界中の誰でもアクセスできるので、悪意ある人が来る前提で作る必要がある。基本の考え方は「ユーザーから来るものは全部疑う」「守る層を重ねる(多層防御)」「権限は必要最小限」の3つ。

生成AIが書いたコードも、ここに挙げた穴をそのまま含むことがあるので、レビュー時のチェックリストとしても使う。

代表的な攻撃と対策

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

何が起きる: 入力欄などから <script> を埋め込まれ、他のユーザーのブラウザで勝手にJavaScriptが動く → Cookie盗難・なりすまし操作。

対策

  • 表示するときにエスケープする(< → &lt; など)。React / Next.js は {value} で自動エスケープされる
  • dangerouslySetInnerHTML / innerHTML にユーザー入力を渡さない。どうしても必要なら DOMPurify などでサニタイズ
  • href={userInput} に javascript: URL が入らないよう検証する
  • CSP(Content-Security-Policy)ヘッダーで、読み込めるスクリプトの出どころを制限(万一の保険)

2. SQLインジェクション

何が起きる: 入力値がそのままSQL文に組み込まれ、' OR 1=1 -- のような文字列でデータを全件取得・削除される。

対策

  • プレースホルダ(パラメータ化クエリ)を必ず使う。文字列連結でSQLを作らない
// ❌ 危険
db.prepare(`SELECT * FROM users WHERE email = '${email}'`)
// ✅ 安全(Cloudflare D1 の例)
db.prepare("SELECT * FROM users WHERE email = ?").bind(email)
  • ORM(Drizzle / Prisma 等)を使うと基本的に安全。ただし生SQLを書く機能では同じ注意が必要

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

何が起きる: ログイン中のユーザーが罠サイトを開くと、勝手に「送金」「退会」などのリクエストが本物のサイトへ送られる(ブラウザがCookieを自動で付けるため)。

対策

  • Cookie に SameSite=Lax(または Strict) を付ける
  • 状態を変える操作は GET ではなく POST 等にする
  • フォームに CSRFトークンを入れる / Origin ヘッダーを検証する
  • Next.js の Server Actions は Origin チェックが組み込まれている

4. 認証・セッションの不備

何が起きる: パスワード漏洩、セッション乗っ取り、総当たり攻撃。

対策

  • 認証は自作しない。Auth.js / Clerk / Supabase Auth など実績あるものを使う
  • パスワードは平文で保存しない(bcrypt / Argon2 でハッシュ化)
  • セッションCookieは HttpOnly(JSから読めない)・Secure(HTTPSのみ)・SameSite を付ける
  • ログイン試行にレート制限をかける。可能なら多要素認証(MFA)
  • ログアウト・パスワード変更時はセッションを無効化する

5. 認可(アクセス制御)の不備 ← 最も多い

何が起きる: /api/orders/123 の数字を変えるだけで他人のデータが見える・消せる(IDOR)。

対策

  • 「ログインしているか(認証)」だけでなく「この人がこのデータを触っていいか(認可)」をサーバー側で毎回チェック
// ✅ 自分の注文だけに絞る
db.prepare("SELECT * FROM orders WHERE id = ? AND user_id = ?").bind(id, session.userId)
  • 画面でボタンを隠すだけでは対策にならない。APIは直接叩ける
  • 管理者機能は特に厳しくチェックする

6. 入力値検証の不足

対策

  • サーバー側で必ず検証する(フロントの検証はUXのため。簡単に回避できる)
  • Zod などでスキーマを定義し、型・長さ・形式をチェック
  • ファイルアップロードは拡張子・MIMEタイプ・サイズを制限し、ユーザー指定のファイル名をそのまま使わない

7. SSRF・オープンリダイレクト

  • ユーザーが指定したURLへサーバーからアクセスする機能(OGP取得など)は、社内・クラウドのメタデータURLにアクセスされないよう許可リストで制限
  • ?redirect= のようなリダイレクト先は自サイト内のパスだけ許可する(フィッシングに悪用されるため)

設定・インフラ面の対策

HTTPS

  • 全ページHTTPS化し、HTTPはHTTPSへリダイレクト(詳細は HTTP・HTTPS・TLS)
  • HSTSヘッダーで「常にHTTPSで来て」とブラウザに覚えさせる

セキュリティヘッダー

ヘッダー役割
Content-Security-Policy読み込めるスクリプト・画像などの出どころを制限(XSS対策)
Strict-Transport-SecurityHTTPS強制(HSTS)
X-Content-Type-Options: nosniffファイルの種類をブラウザに推測させない
X-Frame-Options / CSP frame-ancestors他サイトの iframe に埋め込ませない(クリックジャッキング対策)
Referrer-Policy遷移先に送るURL情報を制限

設定後は securityheaders.com などで確認できる。

CORS

  • Access-Control-Allow-Origin: * を認証付きAPIで使わない。許可するオリジンを明示する

シークレット管理

依存パッケージ

  • npm audit / Dependabot / Renovate で脆弱性のあるライブラリを検知・更新
  • AIが提案した聞き覚えのないパッケージ名は実在・信頼性を確認してから入れる(偽パッケージ対策)
  • package-lock.json などのロックファイルをコミットする

エラー・ログ

  • 本番でスタックトレースやSQLエラーを画面に出さない(内部構造のヒントになる)
  • ログにパスワード・トークン・個人情報を出力しない
  • 不審なアクセス(ログイン失敗の連続など)は記録して気付けるようにする

レート制限・Bot対策

  • ログイン・問い合わせフォーム・APIに回数制限をかける
  • Cloudflare の WAF / Rate Limiting / Turnstile(CAPTCHA代替)が手軽

バックアップ

  • DBは定期バックアップし、復元できることを確認しておく(D1 は Time Travel で過去に戻せる)

リリース前チェックリスト

  • 全ページHTTPS、HSTS設定済み
  • ユーザー入力を innerHTML / dangerouslySetInnerHTML に入れていない
  • SQLはすべてプレースホルダ or ORM
  • すべてのAPIで「認証」と「認可(本人のデータか)」をサーバー側でチェック
  • セッションCookieに HttpOnly / Secure / SameSite
  • 入力値をサーバー側で検証(Zod等)
  • シークレットがリポジトリ・ブラウザに含まれていない
  • セキュリティヘッダー(CSP等)を設定
  • 本番でエラー詳細を表示していない
  • npm audit で重大な脆弱性なし
  • ログイン・フォームにレート制限
  • バックアップと復元手順がある

参考

  • OWASP Top 10: Webアプリの代表的なリスクのランキング。迷ったらここを見る
  • IPA「安全なウェブサイトの作り方」: 日本語で読める定番資料

関連