security
S
信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
OWASP Top 10 2025:Webアプリの脆弱性を正しく理解する
OWASP Top 10 2025版の各カテゴリをコード例付きで解説。Broken Access Control・Injection・Cryptographic Failures など、現代のWebアプリが直面するセキュリティリスクと対策を紹介します。
一言結論
OWASP Top 10でA01のBroken Access Controlが長年1位を占める事実が示すように、最も危険な脆弱性は複雑な攻撃ではなく「認可チェックの実装忘れ」という単純なミスであり、全エンドポイントでの認可検証が最優先の対策だ。
OWASP Top 10 2025 一覧
| 順位 | カテゴリ |
|---|---|
| A01 | Broken Access Control(アクセス制御の破綻) |
| A02 | Cryptographic Failures(暗号化の失敗) |
| A03 | Injection(インジェクション) |
| A04 | Insecure Design(安全でない設計) |
| A05 | Security Misconfiguration(セキュリティの設定ミス) |
| A06 | Vulnerable Components(脆弱なコンポーネント) |
| A07 | Identification & Authentication Failures(認証の失敗) |
| A08 | Software & Data Integrity Failures(整合性の失敗) |
| A09 | Security Logging & Monitoring Failures(監視の失敗) |
| A10 | Server-Side Request Forgery(SSRF) |
A01: Broken Access Control
最も多く報告される脆弱性。「自分に見えてはいけないデータが見える」問題。
// ❌ ユーザーIDをクライアントから信頼する
app.get("/orders/:orderId", async (req, res) => {
const order = await db.orders.findById(req.params.orderId);
res.json(order); // 他人の注文も取れてしまう
});
// ✅ セッションのユーザーIDと照合する
app.get("/orders/:orderId", authenticate, async (req, res) => {
const order = await db.orders.findOne({
id: req.params.orderId,
userId: req.user.id, // ← 認証済みユーザーの注文のみ返す
});
if (!order) return res.status(404).json({ error: "Not found" });
res.json(order);
});
A02: Cryptographic Failures
暗号化の不備、平文での機密情報保存。
// ❌ パスワードを MD5/SHA1 でハッシュ(破られる)
const hash = crypto.createHash("md5").update(password).digest("hex");
// ✅ bcrypt / Argon2 を使う
import bcrypt from "bcrypt";
const hash = await bcrypt.hash(password, 12); // cost factor 12以上
const isValid = await bcrypt.compare(inputPassword, hash);
❌ HTTP で個人情報を送信
✅ HTTPS(TLS 1.2+)を強制
❌ 接続文字列をコードに直書き
✅ 環境変数 / Secrets Manager を使用
A03: Injection
SQLインジェクション・コマンドインジェクション・LDAP インジェクションなど。
// ❌ SQL インジェクション
const query = `SELECT * FROM users WHERE email = '${email}'`;
// email = "' OR '1'='1" でスキップできる
// ✅ プリペアドステートメント
const user = await db.query(
"SELECT * FROM users WHERE email = $1",
[email]
);
A05: Security Misconfiguration
デフォルト設定のままの使用、不要なポートの開放など。
// Express のセキュリティヘッダー(helmet を使う)
import helmet from "helmet";
app.use(helmet());
// X-Frame-Options, X-XSS-Protection, HSTS などを自動設定
// ❌ 詳細なエラーメッセージを本番で返す
app.use((err, req, res, next) => {
res.status(500).json({ error: err.stack }); // スタックトレースが漏洩
// ✅ 本番では汎用エラーメッセージを返す
app.use((err, req, res, next) => {
logger.error(err);
res.status(500).json({ error: "Internal Server Error" });
});
A06: Vulnerable Components
脆弱性のある npm/pip パッケージの使用。
# 依存関係の脆弱性チェック
npm audit
pip audit
# 自動修正
npm audit fix
# CI での自動チェック(GitHub Dependabot を有効化)
# .github/dependabot.yml
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
A07: Identification & Authentication Failures
// ✅ アカウントロックアウト
const MAX_ATTEMPTS = 5;
const LOCKOUT_DURATION = 15 * 60; // 15分
app.post("/login", async (req, res) => {
const attempts = await redis.incr(`login_attempts:${req.body.email}`);
if (attempts === 1) {
await redis.expire(`login_attempts:${req.body.email}`, LOCKOUT_DURATION);
}
if (attempts > MAX_ATTEMPTS) {
return res.status(429).json({ error: "Too many attempts. Try again later." });
}
// ... ログイン処理
});
// ✅ パスワードリセットトークンに有効期限を設ける
const token = crypto.randomBytes(32).toString("hex");
await db.resetTokens.create({
token: hash(token),
userId,
expiresAt: new Date(Date.now() + 60 * 60 * 1000), // 1時間
});
A10: SSRF(Server-Side Request Forgery)
サーバーが内部リソースにアクセスさせられる攻撃。
// ❌ ユーザー入力の URL にリクエストを送る
app.post("/fetch", async (req, res) => {
const data = await fetch(req.body.url); // http://169.254.169.254/metadata などが叩ける
res.json(await data.json());
});
// ✅ 許可された URL のみを受け付ける
const ALLOWED_HOSTS = ["api.example.com", "cdn.example.com"];
app.post("/fetch", async (req, res) => {
const url = new URL(req.body.url);
if (!ALLOWED_HOSTS.includes(url.hostname)) {
return res.status(400).json({ error: "Disallowed host" });
}
const data = await fetch(url);
res.json(await data.json());
});
まとめ
最低限押さえるべき対策:
- すべての API で認可チェックを実装する(A01)
- パスワードは bcrypt/Argon2 でハッシュする(A02)
- プリペアドステートメントを使う(A03)
- helmet でセキュリティヘッダーを設定する(A05)
npm auditを CI に組み込む(A06)