月曜日の朝。オフィスに見慣れない人物がいた。
Yuki: 「紹介するよ。セキュリティコンサルタントの佐久間さん。以前一緒に仕事をしたことがある、腕利きのエシカルハッカーだ。」
佐久間: 「佐久間です。ShimaLinkのセキュリティ監査を担当します。まず言っておくと、私はこれから皆さんのシステムを”攻撃”します。もちろん許可を得た上で、ですけどね。」
あなた: 「もうXSSもSQLインジェクションも直したし、暗号化もバッチリなんですけど…」
佐久間: 「それは素晴らしい。でもセキュリティには”見えない穴”がある。自分で作ったシステムの脆弱性は、自分では見つけにくいんです。だからこそ第三者の目が必要なんですよ。」
佐久間さんがノートPCを開き、ShimaLinkにアクセスする。数分間無言で操作した後、静かに画面を見せた。
佐久間: 「…3つ見つけました。」
あなた: 「え、もう?」
佐久間: 「OWASP Top 10——Webアプリケーションの最も重要なセキュリティリスクのリスト——を順番にチェックしただけです。体系的なアプローチが重要なんですよ。」
佐久間さんが指摘したのは、チームが見落としていた3つの脆弱性だった。
そして、もう一つ気になることがあるという。
佐久間: 「ログを見ました。Shadowの攻撃パターン…どこかで見たことがある気がする。」
Yuki: 「…心当たりがあるの?」
佐久間: 「まだ確証はない。でもこの手口、ある意味”教育的”なんですよ。致命的な損害を与えるつもりなら、もっと効率的な方法があった。まるで…誰かに学ばせようとしているみたいだ。」
チーム全員が顔を見合わせる。Shadowの正体に、手がかりが近づいている。
セキュリティ監査の全容を、体系的に学んでいこう。
OWASP Top 10 — Webセキュリティの必修科目
OWASP(Open Web Application Security Project)が発表するTop 10は、Webアプリケーションに対する最も重要なセキュリティリスクのリストです。
佐久間: 「OWASP Top 10は、Webセキュリティの”教科書の目次”みたいなもの。これを知らないのは、医者が人体の臓器を知らないのと同じだ。」
OWASP Top 10(2021年版)
| # | リスク | 概要 | ShimaLinkでの対応 |
|---|---|---|---|
| A01 | アクセス制御の不備 | 認可チェックの欠如 | RBAC導入予定 |
| A02 | 暗号化の失敗 | 平文保存、弱い暗号 | bcrypt + AES導入済み |
| A03 | インジェクション | SQLi, XSS, コマンドi | パラメータ化 + CSP導入済み |
| A04 | 安全でない設計 | セキュリティを考慮しない設計 | 脅威モデリング実施済み |
| A05 | セキュリティの設定ミス | デフォルト設定のまま運用 | helmet導入済み |
| A06 | 脆弱で古いコンポーネント | 更新されていないライブラリ | npm audit定期実施 |
| A07 | 認証の不備 | 弱い認証メカニズム | MFA検討中 |
| A08 | ソフトウェアとデータの整合性障害 | 検証なしのアップデート | CI/CDパイプライン強化 |
| A09 | セキュリティログと監視の失敗 | ログ不足、異常検知なし | ログ監視導入済み |
| A10 | SSRF(サーバーサイドリクエストフォージェリ) | サーバーから不正なリクエスト | URL検証導入予定 |
A01: アクセス制御の不備
最も多い脆弱性。認可チェックが不十分で、本来アクセスできないデータや機能にアクセスできてしまう。
// 脆弱: ユーザーIDだけでデータを取得(所有権チェックなし)
app.get('/api/user/:id', async (req, res) => {
const user = await prisma.user.findUnique({ where: { id: req.params.id } });
res.json(user); // 他人のデータも見えてしまう
});
// 安全: ログインユーザーと一致するか確認
app.get('/api/user/:id', authMiddleware, async (req, res) => {
if (req.user.id !== req.params.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'アクセス権限がありません' });
}
const user = await prisma.user.findUnique({ where: { id: req.params.id } });
res.json(user);
});
A05: セキュリティの設定ミス
デフォルト設定、不要な機能の有効化、不適切なエラー処理などが原因。
// よくある設定ミス
// 1. デバッグモードが本番で有効
app.set('env', 'development'); // 本番では 'production'
// 2. 不要なHTTPヘッダーの露出
// X-Powered-By: Express → サーバー技術が分かる
app.disable('x-powered-by'); // または helmet() を使う
// 3. ディレクトリリスティングが有効
app.use(express.static('public', { dotfiles: 'deny' }));
// 4. CORSが全開放
app.use(cors()); // → すべてのオリジンからアクセス可能
// 正しいCORS設定
app.use(cors({
origin: ['https://shimalink.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
}));
A06: 脆弱で古いコンポーネント
使用しているライブラリに既知の脆弱性がある場合。
# 脆弱性チェック
npm audit
# 結果例:
# found 3 vulnerabilities (1 low, 1 moderate, 1 high)
# 自動修正
npm audit fix
# 破壊的変更を含む修正
npm audit fix --force # 注意: テストを必ず実行すること
佐久間: 「依存ライブラリの脆弱性は”時限爆弾”みたいなもの。定期的にチェックして、問題があればすぐに更新する。」
A09: セキュリティログと監視の失敗
攻撃が起きても気づけなければ、被害は拡大する一方です。
// 記録すべきイベント
const securityEvents = {
LOGIN_SUCCESS: 'ログイン成功',
LOGIN_FAILURE: 'ログイン失敗',
ACCESS_DENIED: 'アクセス拒否',
DATA_EXPORT: 'データエクスポート',
ADMIN_ACTION: '管理者操作',
SUSPICIOUS_INPUT: '不審な入力検知'
};
function logSecurityEvent(event, details) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
event: event,
ip: details.ip,
userId: details.userId,
details: details.message
}));
}
ポイント
- OWASP Top 10はWebセキュリティの基本知識として必須
- アクセス制御の不備(A01)が最も多い脆弱性
- 設定ミス、古いライブラリ、ログ不足も重大なリスク
- 定期的にnpm auditでライブラリの脆弱性をチェックする
セキュリティチェックリスト
体系的にセキュリティを検証するためのチェックリストを作成します。佐久間さんが実際の監査で使っている項目に基づいています。
佐久間: 「チェックリストがあれば、“確認し忘れ”がなくなる。パイロットが離陸前に必ずチェックリストを確認するのと同じだよ。」
認証・認可チェックリスト
## 認証(Authentication)
- [ ] パスワードはbcrypt/Argon2でハッシュ化して保存している
- [ ] パスワードポリシーがある(8文字以上、複雑性要件)
- [ ] ログイン失敗時のレート制限がある(5回失敗でロック等)
- [ ] セッションIDは十分にランダムで推測不能
- [ ] ログアウト時にセッションを確実に無効化している
- [ ] 「パスワードを忘れた」機能が安全に実装されている
- [ ] パスワードリセットトークンは一度限り、有効期限付き
## 認可(Authorization)
- [ ] すべてのAPIエンドポイントで認可チェックを行っている
- [ ] ユーザーは自分のデータにのみアクセスできる
- [ ] 管理者機能は管理者のみアクセス可能
- [ ] IDの書き換え(IDOR)で他人のデータにアクセスできない
- [ ] 水平権限昇格(同レベルの他ユーザーのデータ)を防いでいる
- [ ] 垂直権限昇格(管理者権限の取得)を防いでいる
入力・出力チェックリスト
## 入力検証
- [ ] すべてのユーザー入力をサーバー側で検証している
- [ ] SQLクエリはパラメータ化/ORMを使用している
- [ ] ファイルアップロードの種類・サイズを制限している
- [ ] URLリダイレクトのホワイトリスト検証をしている
## 出力エスケープ
- [ ] HTMLコンテキストでエスケープしている
- [ ] JavaScriptコンテキストでエスケープしている
- [ ] URLパラメータをエンコードしている
- [ ] CSPヘッダーを設定している
通信・データ保護チェックリスト
## 通信
- [ ] すべてのページでHTTPSを強制している
- [ ] HSTSヘッダーを設定している
- [ ] TLS 1.2以上を使用している
- [ ] HTTPからHTTPSへリダイレクトしている
## データ保護
- [ ] 機密データは保存時に暗号化している
- [ ] バックアップも暗号化している
- [ ] 不要になったデータは安全に削除している
- [ ] ログに機密情報(パスワード、トークン)を記録していない
セキュリティヘッダーチェックリスト
## HTTPセキュリティヘッダー
- [ ] Content-Security-Policy
- [ ] X-Content-Type-Options: nosniff
- [ ] X-Frame-Options: DENY(またはSAMEORIGIN)
- [ ] X-XSS-Protection: 0(CSPがあればこちらは無効化推奨)
- [ ] Strict-Transport-Security
- [ ] Referrer-Policy: strict-origin-when-cross-origin
// helmetで一括設定
const helmet = require('helmet');
app.use(helmet());
// これだけで多くのセキュリティヘッダーが設定される
CSRF対策チェックリスト
## CSRF(Cross-Site Request Forgery)
- [ ] 状態変更リクエスト(POST/PUT/DELETE)にCSRFトークンを使用
- [ ] CSRFトークンはリクエストごとに検証
- [ ] SameSite Cookie属性を設定している
// CSRFトークンの実装例
const csrf = require('csurf');
app.use(csrf({ cookie: true }));
// フォームにトークンを埋め込む
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
佐久間さんがShimaLinkで見つけた3つの問題
1. IDORの脆弱性
/api/shops/123 → 他人のショップ情報を編集できた
→ 所有者チェックの追加で修正
2. レート制限の欠如
ログインAPIに回数制限なし → ブルートフォース可能
→ express-rate-limitの導入で修正
3. CORS設定の不備
すべてのオリジンからのアクセスを許可していた
→ ホワイトリスト方式に変更
ポイント
- チェックリストを使えば確認漏れを防げる
- 認証、認可、入出力、通信、ヘッダーを体系的に確認する
- 自分では見つけにくい脆弱性があるため、第三者の目が重要
- helmetライブラリでセキュリティヘッダーを一括設定できる
ペネトレーションテストの基本
ペネトレーションテスト(ペンテスト)は、実際の攻撃者と同じ手法でシステムの脆弱性を発見するテスト手法です。
佐久間: 「泥棒を捕まえるには、泥棒の考え方を知る必要がある。ペンテストは”合法的な泥棒”のようなものだ。」
ペネトレーションテストとは
| 項目 | 説明 |
|---|---|
| 目的 | 実際に攻撃を試み、脆弱性を発見する |
| 実施者 | セキュリティ専門家(エシカルハッカー) |
| 前提 | 必ず書面による許可を得てから実施 |
| 結果 | 脆弱性レポートと修正推奨事項 |
重要: 許可なく他人のシステムをテストすることは犯罪(不正アクセス禁止法違反)です。
ペンテストの種類
| 種類 | テスターが知っている情報 | 特徴 |
|---|---|---|
| ブラックボックス | なし(URLのみ) | 外部攻撃者の視点 |
| ホワイトボックス | ソースコード、設計書すべて | 内部者の視点、網羅的 |
| グレーボックス | 一部の情報(API仕様等) | バランスが良い |
佐久間さんがShimaLinkに対して行ったのはグレーボックステストです。
ペンテストの5つのフェーズ
Phase 1: 偵察(Reconnaissance)
ターゲットの情報を収集する。
# 公開情報の収集(OSINTの例)
# DNSレコードの確認
dig shimalink.com ANY
# サブドメインの列挙
# admin.shimalink.com, api.shimalink.com などが見つかるかも
# 使用技術の特定(レスポンスヘッダーから)
curl -I https://shimalink.com
# → Server: nginx, X-Powered-By: Express
Phase 2: スキャン(Scanning)
脆弱性スキャナーで自動チェック。
# OWASP ZAP(無料のWebアプリスキャナー)
# GUIツールでURLを入力するだけで自動スキャン
# Nikto(Webサーバースキャナー)
nikto -h https://shimalink.com
# npm audit(依存ライブラリの脆弱性)
npm audit
Phase 3: 脆弱性の検証(Exploitation)
発見した脆弱性が実際に悪用可能か確認する。
例: IDORの検証
1. 自分のショップページ: /api/shops/123 → 200 OK
2. 他人のショップページ: /api/shops/456 → 200 OK!?
→ アクセスできてしまう = 脆弱性確認
Phase 4: アクセスの維持と深掘り
一つの脆弱性から、どこまでアクセスを拡大できるか確認する。
Phase 5: レポート作成
発見した脆弱性、影響度、再現手順、修正推奨事項をレポートにまとめる。
開発者ができる簡易セキュリティテスト
専門家に頼む前に、自分たちでできる基本的なテスト。
## 簡易セキュリティテスト手順
1. 認証テスト
- [ ] ログインせずにAPIにアクセスしてみる
- [ ] 期限切れのトークンでアクセスしてみる
2. 認可テスト
- [ ] 他のユーザーのIDでAPIにアクセスしてみる
- [ ] 一般ユーザーで管理者APIにアクセスしてみる
3. 入力テスト
- [ ] 各フォームにXSSペイロードを入力してみる
- [ ] 長い文字列(10000文字)を入力してみる
- [ ] 特殊文字(<>"';&|)を入力してみる
4. 設定テスト
- [ ] HTTPでアクセスしたらHTTPSにリダイレクトされるか
- [ ] セキュリティヘッダーが返されているか
- [ ] エラーページに内部情報が含まれていないか
ツール紹介
| ツール | 用途 | 無料 |
|---|---|---|
| OWASP ZAP | Webアプリ脆弱性スキャン | 無料 |
| Burp Suite | HTTPプロキシ+スキャン | Community版無料 |
| Nikto | Webサーバースキャン | 無料 |
| sqlmap | SQLインジェクション検出 | 無料 |
| npm audit | Node.js依存関係チェック | 無料 |
佐久間: 「ツールは便利だけど、万能じゃない。ビジネスロジックの脆弱性はツールでは見つけられない。人間の判断力が不可欠だ。」
ポイント
- ペンテストは許可を得た上で行う「合法的な攻撃テスト」
- 偵察→スキャン→検証→深掘り→レポートの5フェーズ
- 開発者でも簡易テストは実施できる
- ツールと人間の判断力の両方が必要
インシデント対応計画
セキュリティ事故は「起きるかどうか」ではなく「いつ起きるか」の問題です。事前に対応計画を策定しておくことで、被害を最小限に抑えられます。
佐久間: 「消防署は火事が起きてから避難計画を考えない。事前に計画があるから、迅速に対応できるんだ。」
インシデント対応の4フェーズ
Phase 1: 準備(Preparation)
↓
Phase 2: 検知と分析(Detection & Analysis)
↓
Phase 3: 封じ込め・根絶・復旧(Containment, Eradication, Recovery)
↓
Phase 4: 事後対応(Post-Incident Activity)
Phase 1: 準備
事故が起きる前に体制を整えます。
## インシデント対応チーム(IRT)
| 役割 | 担当 | 責任 |
|------|------|------|
| インシデントマネージャー | Yuki | 全体指揮、意思決定 |
| 技術リード | あなた | 技術調査、修正実装 |
| コミュニケーション | あなた | ユーザー・外部への通知 |
| ログ分析 | あなた | ログ調査、証拠保全 |
## 連絡先リスト
- チーム: Slackチャンネル #security-incident
- サーバー管理: [クラウドプロバイダー]
- 法務: [顧問弁護士]
- 監督機関: 個人情報保護委員会(必要に応じて)
Phase 2: 検知と分析
異常を素早く検知し、影響範囲を特定します。
// アラート設定の例
const alertConditions = {
// 短時間での大量ログイン失敗
bruteForce: {
condition: 'loginFailures > 10 in 5min from same IP',
severity: 'high',
action: 'IP一時ブロック + アラート送信'
},
// 大量のデータアクセス
dataExfiltration: {
condition: 'API responses > 1000 in 10min from same user',
severity: 'critical',
action: 'セッション無効化 + アラート送信'
},
// 管理者APIへの不正アクセス試行
adminAccess: {
condition: 'non-admin user accessing /admin/*',
severity: 'high',
action: 'ログ記録 + アラート送信'
}
};
影響度の分類
| レベル | 定義 | 対応時間 |
|---|---|---|
| Critical | データ漏洩、サービス停止 | 即座 |
| High | 認証バイパス、権限昇格 | 4時間以内 |
| Medium | 情報漏洩(非機密)、設定ミス | 24時間以内 |
| Low | 軽微な脆弱性 | 次のスプリント |
Phase 3: 封じ込め・根絶・復旧
封じ込め(Containment)
被害の拡大を止めます。
# 即座の対応例
# 1. 攻撃元IPのブロック
iptables -A INPUT -s 攻撃者IP -j DROP
# 2. 侵害されたアカウントの無効化
# DBで直接セッションを無効化
# 3. サービスのメンテナンスモード切り替え
# → ユーザーへの影響を最小限に
根絶(Eradication)
脆弱性そのものを修正します。
- 脆弱性の特定と修正パッチの作成
- バックドアやマルウェアの除去
- 侵害された認証情報のリセット
- 関連するすべてのシステムの確認
復旧(Recovery)
サービスを安全に復旧します。
1. 修正パッチの適用とテスト
2. 段階的なサービス復旧
3. 監視の強化(再発がないか確認)
4. 影響を受けたユーザーへの通知
Phase 4: 事後対応(ポストモーテム)
同じ事故を繰り返さないために振り返りを行います。
## インシデント振り返りテンプレート
### 概要
- 発生日時:
- 検知日時:
- 復旧日時:
- 影響範囲:
### タイムライン
- 02:00 異常なアクセスを検知
- 02:15 チームに通知
- 02:30 メンテナンスモードに切り替え
- 03:00 攻撃経路を特定
- ...
### 根本原因
[何が原因だったか]
### うまくいったこと
- ログ監視が機能し、早期に検知できた
- チームの連携がスムーズだった
### 改善すべきこと
- 入力検証が不十分だった
- インシデント対応手順が文書化されていなかった
### アクションアイテム
- [ ] [担当者] XSSサニタイズの強化(期限: 3/20)
- [ ] [担当者] インシデント対応手順書の作成(期限: 3/25)
- [ ] [担当者] セキュリティ研修の実施(期限: 4/1)
佐久間: 「ポストモーテムは”犯人探し”じゃない。“システム改善”のためにやるんだ。誰かを責めるんじゃなく、プロセスを改善する。」
ShimaLinkのインシデント対応計画
# ShimaLink インシデント対応計画 v1.0
## 1. 検知
- サーバーログの24時間監視
- 異常アクセスの自動アラート(Slack #security-alert)
- npm auditの週次自動実行
## 2. 初動対応(30分以内)
- インシデントマネージャーへの連絡
- 影響範囲の初期評価
- 必要に応じてメンテナンスモード切り替え
## 3. 通知
- 影響を受けたユーザーへの通知(24時間以内)
- 個人情報漏洩の場合、監督機関への報告(72時間以内)
## 4. 復旧後
- ポストモーテムの実施(7日以内)
- 再発防止策の実装
- lessons.mdへの教訓記録
ポイント
- インシデント対応は「事前に計画しておく」ことが最重要
- 準備→検知→封じ込め→事後対応の4フェーズ
- ポストモーテムは犯人探しではなくプロセス改善のため
- 対応計画は定期的に見直し、訓練する
セキュリティ監査のすべての指摘事項が修正された。佐久間さんによる再テストも完了し、ShimaLinkは「合格」の評価を受けた。
[ShimaLink セキュリティ監査レポート]
─────────────────────────────
監査日: 2024年4月
監査者: 佐久間セキュリティコンサルティング
総合評価: 合格(修正後)
対応済み:
✓ XSS対策(入力サニタイズ + CSP)
✓ SQLインジェクション対策(パラメータ化クエリ + ORM)
✓ パスワードハッシュ化(bcrypt)
✓ HTTPS強制(TLS 1.3 + HSTS)
✓ CSRF対策トークン導入
✓ レート制限実装
✓ エラーメッセージの情報漏洩修正
✓ インシデント対応計画策定
推奨事項:
- 定期的なペネトレーションテスト(年2回)
- 依存ライブラリの自動脆弱性スキャン
- セキュリティ研修の継続
─────────────────────────────佐久間: 「よくやった。正直、最初に見たときは穴だらけだったけど、ここまで短期間で改善できたのは大したものだ。」
あなた: 「佐久間さん、Shadowの件…何か分かりましたか?」
佐久間: 「…一つだけ言えることがある。攻撃のログをもう一度精査したんだが、Shadowは意図的に深刻な被害を避けている。データベースを全削除できたのにしなかった。サービスを完全に停止させることもできたのに、しなかった。」
あなた: 「つまり…?」
佐久間: 「これは破壊目的の攻撃じゃない。誰かがShimaLinkチームに”セキュリティの重要性”を教えようとしたように見える。やり方は完全に間違ってるけどね。」
Yuki: 「…。」
Yukiが何かを言いかけて、口をつぐんだ。
危機編が終わる。ShimaLinkはセキュリティという鎧を身にまとい、以前よりはるかに強固になった。
Shadowの正体はまだ分からない。しかし、一つだけ確かなことがある。
あの危機がなければ、ShimaLinkはもっと大きな攻撃で壊滅していたかもしれない。
あなた: 「Yukiさん、次は何を学べばいいですか?」
Yuki: 「…守りは固めた。次は”攻め”の技術だよ。ShimaLinkをスケールさせるための、パフォーマンスとアーキテクチャ。」
Yukiの視線が一瞬だけ、遠くを見ていた気がした。
危機編 完
ShimaLinkは危機を乗り越えた。セキュリティの基礎を身につけたあなたは、次のフェーズへ進む。
🧠 理解度チェック
Q1.OWASP Top 10の2021年版で最も上位にランクされているリスクは?
💡 佐久間さんがShimaLinkで最初に見つけたIDORの脆弱性も、アクセス制御の問題だった。
Q2.ペネトレーションテストを実施する際に最も重要な前提条件は?
💡 佐久間さんも「許可を得た上で攻撃します」と最初に明言していた。
Q3.IDOR(Insecure Direct Object Reference)とは?
💡 佐久間さんがShimaLinkで発見した3つの問題の1つ目がこれだった。
Q4.インシデント対応の最初のフェーズは?
💡 佐久間さんが「消防署は火事の前に避難計画を作る」と言っていた通りだ。
Q5.ポストモーテムの主な目的は?
💡 佐久間さんが「誰かを責めるんじゃなく、プロセスを改善する」と強調していた。
Q6.npm auditコマンドの用途は?
💡 定期的にnpm auditを実行して「時限爆弾」を排除する——佐久間さんのアドバイスだ。
Q7.ブラックボックステストとホワイトボックステストの違いは?
💡 佐久間さんがShimaLinkに対して行ったのは、API仕様など一部の情報を持つグレーボックステストだった。
Q8.セキュリティインシデント発生時、最初にすべきことは?
💡 ShimaLinkの侵害時、最初にサービスをメンテナンスモードに切り替えたのが封じ込めだった。
❓ よくある質問
OWASP ZAPのインストールと使い方は?
OWASP ZAP(Zed Attack Proxy)は無料のWebアプリ脆弱性スキャナーです。 **インストール:** 1. [zaproxy.org](https://www.zaproxy.org/) からダウンロード 2. macOS: `brew install --cask owasp-zap` **基本的な使い方:** 1. ZAPを起動 2. 「Automated Scan」を選択 3. URLを入力(例: http://localhost:3000) 4. 「Attack」をクリック 5. 結果を確認(High/Medium/Low/Informational) **重要**: 自分の開発環境でのみ使用してください。他人のサイトへのスキャンは不正アクセスとなります。
セキュリティヘッダーが正しく設定されているか確認する方法は?
以下の方法で確認できます: **1. curlコマンド:** ```bash curl -I https://shimalink.com # レスポンスヘッダーを確認 ``` **2. ブラウザのDevTools:** - F12 → Network → リクエストを選択 → Headers **3. オンラインツール:** - SecurityHeaders.com: URLを入力するだけでA〜Fで評価 - Mozilla Observatory: 総合的なセキュリティ評価 **確認すべきヘッダー:** ``` Content-Security-Policy: ... Strict-Transport-Security: max-age=31536000 X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin ```
レート制限の実装方法は?
express-rate-limitパッケージを使います。 ```bash npm install express-rate-limit ``` ```javascript const rateLimit = require('express-rate-limit'); // 一般的なAPI制限 const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15分 max: 100, // 15分あたり100リクエスト message: 'リクエスト回数が上限に達しました' }); app.use('/api/', apiLimiter); // ログイン用の厳しい制限 const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, // 15分あたり5回まで message: 'ログイン試行回数が上限に達しました' }); app.use('/api/login', loginLimiter); ``` 佐久間さんが指摘した「レート制限の欠如」はこれで解決できます。
CORSの正しい設定方法がわからない
CORS(Cross-Origin Resource Sharing)は「どのオリジンからのアクセスを許可するか」を制御します。 ```bash npm install cors ``` ```javascript const cors = require('cors'); // 悪い例: 全開放 app.use(cors()); // すべてのオリジンからアクセス可能 // 良い例: ホワイトリスト const corsOptions = { origin: [ 'https://shimalink.com', 'https://www.shimalink.com' ], methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, maxAge: 86400 // preflight結果を24時間キャッシュ }; app.use(cors(corsOptions)); ``` 開発環境ではlocalhostも追加します: ```javascript if (process.env.NODE_ENV === 'development') { corsOptions.origin.push('http://localhost:3000'); } ```
CSRF対策のCSRFトークンとは?
CSRFトークンは、リクエストが正規のフォームから送信されたことを検証するランダムな文字列です。 **仕組み:** ``` 1. サーバーがフォーム表示時にランダムなトークンを生成 2. フォームの隠しフィールドにトークンを埋め込み 3. フォーム送信時にトークンも一緒に送信 4. サーバーがトークンを検証 → 一致すれば正規のリクエスト ``` ```javascript // Express + csurf const csrf = require('csurf'); const csrfProtection = csrf({ cookie: true }); app.get('/form', csrfProtection, (req, res) => { res.send(`<form action="/submit" method="POST"> <input type="hidden" name="_csrf" value="${req.csrfToken()}"> <button>送信</button> </form>`); }); app.post('/submit', csrfProtection, (req, res) => { res.send('トークン検証OK'); }); ```
個人情報漏洩時に報告義務はある?
日本では**個人情報保護法**に基づく報告義務があります。 **報告が必要なケース:** - 要配慮個人情報の漏洩 - 財産的被害が生じるおそれのある漏洩 - 不正な目的をもって行われた漏洩 - 1,000人を超える漏洩 **報告先と期限:** - **個人情報保護委員会**: 速報(事態を知った日から3〜5日以内)+確報(30日以内) - **本人への通知**: 速やかに **ShimaLinkの場合:** メールアドレス142件の漏洩は報告対象です。不正アクセスによる漏洩のため、件数に関わらず報告義務があります。 **注意**: 具体的な対応は弁護士に相談してください。
セキュリティ監査は自分たちでもできる?
基本的なチェックは自分たちでも可能です。 **自分たちでできること:** - セキュリティチェックリストに基づくレビュー - npm auditによる依存関係チェック - OWASP ZAPによる自動スキャン - コードレビューでのセキュリティ観点チェック - セキュリティヘッダーの確認 **専門家に依頼すべきこと:** - 本格的なペネトレーションテスト - ビジネスロジックの脆弱性評価 - ソースコード監査 - コンプライアンス評価 **おすすめアプローチ:** 1. まず自分たちで基本チェックリストを実施 2. 自動ツールでスキャン 3. 重要なマイルストーンで専門家に依頼
依存ライブラリの自動脆弱性チェックを設定するには?
CI/CDパイプラインに組み込むか、GitHub/GitLabの機能を使います。 **GitHub Dependabot:** ```yaml # .github/dependabot.yml version: 2 updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 10 ``` **GitHub Actions:** ```yaml # .github/workflows/security.yml name: Security Audit on: schedule: - cron: '0 9 * * 1' # 毎週月曜9時 push: branches: [main] jobs: audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm audit --audit-level=high ``` これにより脆弱性が見つかるとPull Requestやアラートが自動生成されます。
IDORの防止方法は?
IDOR(Insecure Direct Object Reference)は認可チェックで防ぎます。 ```javascript // 脆弱: IDだけでアクセス app.get('/api/shops/:id', async (req, res) => { const shop = await prisma.shop.findUnique({ where: { id: parseInt(req.params.id) } }); res.json(shop); }); // 安全: 所有者チェック付き app.get('/api/shops/:id', authMiddleware, async (req, res) => { const shop = await prisma.shop.findFirst({ where: { id: parseInt(req.params.id), ownerId: req.user.id // ← 所有者チェック } }); if (!shop) { return res.status(404).json({ error: '見つかりません' }); } res.json(shop); }); ``` **ポイント**: 404を返す(403だと「データは存在するが権限がない」と情報漏洩になる)
Shadowの正体のヒントはある?
Chapter 15の時点で分かっていること: - 攻撃は計画的だが、致命的な被害を意図的に避けている - データベースの全削除やサービス停止が可能だったのにしなかった - 佐久間さんは「教育的」と評した - 攻撃の手口は体系的で、セキュリティの教科書に沿っている - Yukiが何か心当たりがある様子 **佐久間さんの分析:** 「これは破壊目的の攻撃じゃない。誰かがShimaLinkチームにセキュリティの重要性を教えようとしているように見える。」 Shadowの正体は今後のチャプターで明らかになります。