--- id: security-owasp-api-top10 title: OWASP API Top 10 — modern API security category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [security, api, vibe-coding] tech_stack: { language: "TS", applicable_to: ["Backend", "Security"] } applied_in: [] aliases: [OWASP API, BOLA, broken authorization, mass assignment, API security, 2023] --- # OWASP API Top 10 (2023) > API 의 가장 흔한 vulnerability. **BOLA, broken auth, mass assignment, ...**. ## 📖 핵심 개념 - 매 endpoint 가 attack surface. - Web OWASP 와 다름 (API-specific). - Access control 가 가장 흔한 problem. ## 💻 코드 패턴 ### API1: BOLA (Broken Object Level Authorization) ```ts // ❌ User A 가 User B 의 data 가져옴 app.get('/api/users/:id', (req, res) => { const user = await db.users.findById(req.params.id); res.json(user); }); // ✅ Authorization check app.get('/api/users/:id', (req, res) => { const user = await db.users.findById(req.params.id); if (user.id !== req.user.id && !req.user.isAdmin) { return res.status(403).json({ error: 'forbidden' }); } res.json(user); }); ``` → 가장 흔한 API bug. Object 의 ownership 검증. ### API2: Broken Authentication ```ts // ❌ Weak password / no rate limit app.post('/login', async (req, res) => { const user = await db.users.findOne({ email: req.body.email }); if (user && user.password === req.body.password) { return res.json({ token: '...' }); } }); // ✅ app.post('/login', rateLimit({ max: 5 }), async (req, res) => { const user = await db.users.findOne({ email: req.body.email }); if (user && await bcrypt.compare(req.body.password, user.passwordHash)) { return res.json({ token: signJWT(user) }); } await sleep(1000); // timing attack 방지 res.status(401).json({ error: 'invalid' }); }); ``` → Bcrypt + rate limit + 시간 const. ### API3: BOPLA (Broken Object Property Level Authorization) ```ts // ❌ User 가 자기 role 변경 app.patch('/api/users/:id', async (req, res) => { await db.users.update(req.params.id, req.body); }); // → req.body 가 { role: 'admin' } 일 수. // ✅ Whitelist const ALLOWED = ['name', 'email', 'avatar']; const update = pick(req.body, ALLOWED); await db.users.update(req.params.id, update); ``` → Mass assignment 방지. ### API4: Unrestricted Resource Consumption ```ts // ❌ 무한 result app.get('/api/items', async (req, res) => { const items = await db.items.find({}); // 매 item. res.json(items); }); // ✅ Pagination + limit app.get('/api/items', async (req, res) => { const limit = Math.min(Number(req.query.limit) || 50, 100); const items = await db.items.find({}).limit(limit); res.json(items); }); ``` ### API5: Broken Function Level Authorization ```ts // ❌ Admin endpoint 가 admin check 안 app.delete('/api/users/:id', (req, res) => { await db.users.delete(req.params.id); }); // ✅ app.delete('/api/users/:id', requireAdmin, (req, res) => { await db.users.delete(req.params.id); }); function requireAdmin(req, res, next) { if (!req.user?.isAdmin) return res.status(403).end(); next(); } ``` ### API6: Unrestricted Access to Sensitive Business Flows ```ts // ❌ 1 user 가 매 product 의 1 차 buy. // → Reseller bot 가 모두 buy. // ✅ Per-user limit + CAPTCHA + behavior detect if (await purchaseCount(req.user.id, productId, '1h') > 1) { return res.status(429).end(); } ``` → Business logic abuse. ### API7: Server-Side Request Forgery (SSRF) ```ts // ❌ User 가 임의 URL fetch app.post('/api/preview', async (req, res) => { const r = await fetch(req.body.url); res.json(await r.text()); }); // → http://169.254.169.254/ (AWS metadata). // → http://localhost/admin. // ✅ Whitelist + DNS resolve check const url = new URL(req.body.url); if (!ALLOWED_HOSTS.includes(url.host)) return res.status(400).end(); const ip = await dns.resolve(url.host); if (isPrivateIP(ip)) return res.status(400).end(); const r = await fetch(req.body.url); ``` ### API8: Security Misconfiguration ``` - Debug mode in production. - Default credentials. - CORS '*' (production). - TLS 약 (1.0, 1.1). - Verbose error message. - No security header. → Helmet (Node), Rails defaults, etc. ``` ```ts import helmet from 'helmet'; app.use(helmet()); ``` ### API9: Improper Inventory Management ``` - v1 API 가 deprecated 가 still up. - Test endpoint 가 production. - 매 endpoint 의 schema doc. → OpenAPI spec + audit. ``` ### API10: Unsafe Consumption of APIs ```ts // ❌ External API 의 response 가 trust const user = await externalAPI.getUser(id); const html = `
Welcome ${user.name}
`; // XSS. // ✅ Sanitize / validate const user = await externalAPI.getUser(id); const safe = sanitizeHTML(user.name); ``` → External API 도 input. ### Common patterns #### JWT 함정 ```ts // ❌ Algorithm none jwt.verify(token, ''); // 어떤 token 도 valid. // ✅ jwt.verify(token, SECRET, { algorithms: ['RS256'] }); ``` #### Rate limit ```ts import rateLimit from 'express-rate-limit'; app.use('/api/', rateLimit({ windowMs: 60 * 1000, max: 100, // 100 req / min / IP })); // Stricter for sensitive app.post('/api/login', rateLimit({ max: 5, windowMs: 15 * 60 * 1000 }), ...); ``` #### Input validation (Zod) ```ts const schema = z.object({ email: z.string().email(), age: z.number().int().min(18), }); app.post('/api/users', (req, res) => { const result = schema.safeParse(req.body); if (!result.success) return res.status(400).json(result.error); // result.data 가 typed + sanitized. }); ``` → Strong typing + validation. #### SQL injection (Prisma / Drizzle) ```ts // ❌ String concat const r = await db.query(`SELECT * FROM users WHERE id = ${req.params.id}`); // ✅ Parameterized const r = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]); // Prisma 가 자동 safe const user = await prisma.user.findUnique({ where: { id: req.params.id } }); ``` #### CORS ```ts import cors from 'cors'; app.use(cors({ origin: ['https://app.example.com'], credentials: true, })); ``` → '*' 가 production X. ### Audit ```bash # OWASP ZAP docker run owasp/zap2docker-stable zap-baseline.py -t https://api.example.com # Burp Suite # Postman + automated test ``` ### Bug bounty ``` HackerOne, Bugcrowd. - Researcher 가 vulnerability 발견. - Reward 가 severity 따라. - Responsible disclosure. → Critical security fence. ``` → [[Security_Bug_Bounty]]. ### Pen testing ``` Annual / quarterly: - 외부 firm 가 attack 시도. - Report. - Fix + retest. ``` → [[Security_Pen_Testing]]. ### Monitoring ``` - Failed login (brute force). - Unusual request pattern. - 4xx / 5xx spike. - Geo anomaly. → Datadog / Sentry / SIEM. ``` ### Common 함정 ``` - Test in production: skip security check. - "내부 API 만" 가정: not. - Auth 가 client 만: server 도. - Rate limit per IP only: per user 도. - CORS '*' + cookie: CSRF. - Verbose error: leak info. ``` ## 🤔 의사결정 기준 | 위협 | 방어 | |---|---| | BOLA | Authorization check | | Mass assignment | Whitelist | | Brute force | Rate limit + bcrypt | | SSRF | URL whitelist + IP filter | | SQL injection | Parameterized / ORM | | XSS | Sanitize + output encode | | CSRF | Token + SameSite cookie | | Verbose error | Generic message | ## ❌ 안티패턴 - **No authorization (BOLA)**: data leak. - **Bcrypt 없음**: rainbow table. - **String concat SQL**: injection. - **CORS '*'**: CSRF. - **No rate limit**: brute force. - **Verbose error**: info leak. - **Production debug**: stack trace leak. ## 🤖 LLM 활용 힌트 - BOLA 가 #1 흔한 bug. - Whitelist > blacklist. - Bcrypt + rate limit + JWT (algorithm). - ZAP / Burp 가 audit. ## 🔗 관련 문서 - [[Security_OWASP_Top_10_Practical]] - [[Security_Auth_Authz_Patterns]] - [[Security_Input_Validation]]