--- id: security-owasp-top-10-practical title: OWASP Top 10 — 실전 방어 체크리스트 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [security, owasp, vibe-coding] tech_stack: { language: "TS / 다양", applicable_to: ["Backend", "Frontend"] } applied_in: [] aliases: [OWASP, A01, broken access control, injection, SSRF, IDOR] --- # OWASP Top 10 (2021) 실전 > 매년 거의 같은 카테고리. **A01 Broken Access Control = 인증 / 권한 검증 누락**이 1위. 코드 안 검증 위치 (controller / service / DB) + 자동 테스트. ## 📖 핵심 개념 - A01 Broken Access Control - A02 Cryptographic Failures - A03 Injection (SQLi, NoSQLi, OS, LDAP) - A04 Insecure Design - A05 Security Misconfiguration - A06 Vulnerable Components - A07 Identification & Authentication Failures - A08 Data Integrity (CI/CD, deserialization) - A09 Logging / Monitoring Failures - A10 SSRF ## 💻 코드 패턴 ### A01 — 권한 검증 (IDOR 방어) ```ts // ❌ app.get('/orders/:id', async (req, res) => { const order = await db.orders.find(req.params.id); res.json(order); // 다른 사용자 거 보임 }); // ✅ app.get('/orders/:id', authRequired, async (req, res) => { const order = await db.orders.find({ id: req.params.id, userId: req.user.id }); if (!order) return res.status(404).end(); res.json(order); }); ``` ```sql -- DB 레벨 추가 방어 (RLS) CREATE POLICY user_isolation ON orders FOR SELECT USING (user_id = current_setting('app.user_id')::UUID); ``` ### A02 — 암호화 ```ts // 비밀번호 = bcrypt / argon2id (절대 SHA256 만 X) import argon2 from 'argon2'; const hash = await argon2.hash(password, { type: argon2.argon2id }); const ok = await argon2.verify(hash, candidate); // 데이터 암호화 = AES-GCM import { createCipheriv, randomBytes } from 'node:crypto'; const iv = randomBytes(12); const cipher = createCipheriv('aes-256-gcm', key, iv); const enc = Buffer.concat([cipher.update(plain), cipher.final()]); const tag = cipher.getAuthTag(); // 저장: iv + enc + tag ``` ### A03 — Injection ```ts // ❌ SQL injection db.query(`SELECT * FROM users WHERE email = '${email}'`); // ✅ Parameterized db.query('SELECT * FROM users WHERE email = $1', [email]); // ✅ ORM db.users.find({ where: { email } }); ``` ```ts // Command injection exec(`convert ${userInput} out.png`); // ❌ execFile('convert', [userInput, 'out.png']); // ✅ shell 안 거침 ``` ```ts // NoSQL injection db.users.find({ email: req.body.email }); // body 가 { $ne: null } 이면 모두 반환 // → 명시적 검증 db.users.find({ email: String(req.body.email) }); ``` ### A04 — Insecure Design (rate limiting, lockout) ```ts // 로그인 5회 실패 → 15분 lockout const attempts = await redis.incr(`login:${email}`); await redis.expire(`login:${email}`, 900); if (attempts > 5) throw new Error('Account locked'); ``` ### A05 — Misconfiguration ```ts // 안 쓰는 endpoint / debug / default password 끄기 app.disable('x-powered-by'); helmet(); // 보안 헤더 자동 // CORS 명시 origin cors({ origin: ['https://app.com'], credentials: true }); ``` ### A06 — Vulnerable Components ```bash npm audit npm audit fix # 또는 Snyk / Dependabot 자동 ``` ### A07 — Auth ```ts // MFA 활성 // Strong password rules: NIST 2025 = 길이만 (8+) — 복잡도 X // JWT exp 짧게 + refresh // HttpOnly + Secure + SameSite=Strict cookie res.cookie('session', token, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 3600_000, }); ``` ### A08 — Integrity ```ts // CI 가 lockfile 없이 install 금지 // npm ci --ignore-scripts (postinstall 악성 차단) // SBOM 생성 // JWT signed + verify with library (직접 X) ``` ### A09 — Logging ```ts log.warn('login failed', { email, ip, reason }); // 평문 PW 절대 X // 보안 이벤트 SIEM 으로 ``` ### A10 — SSRF ```ts // ❌ 사용자 URL 그대로 fetch fetch(req.body.url); // ✅ allowlist + private IP 차단 const allowed = ['api.partner.com']; const u = new URL(req.body.url); if (!allowed.includes(u.host)) throw new Error('not allowed'); const ip = await dns.resolve(u.host); if (isPrivateIP(ip)) throw new Error('private ip'); ``` ### CSP 헤더 ```ts helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", 'https://cdn.example.com'], imgSrc: ["'self'", 'data:', 'https:'], connectSrc: ["'self'", 'https://api.example.com'], }, }, }); ``` ## 🤔 의사결정 기준 | 위험 | 우선순위 | 도구 | |---|---|---| | Public API | A01, A03, A07, A10 | | 사용자 데이터 | A02, A09 | | OSS 의존 | A06 (Snyk, Dependabot) | | Admin 패널 | A01, A04 | | 결제 | A02, PCI | | Webhook | HMAC + replay 방어 | ## ❌ 안티패턴 - **인증 = 권한이라고 착각**: 로그인 했다고 모든 거 접근 X. - **Frontend 만 검증**: bypass 가능. - **에러 메시지 상세 (stack)**: 정보 노출. - **SHA256 / MD5 password**: rainbow table. - **JWT secret 공유 / 하드코딩**: leak. - **CORS *** + credentials**: 자동 spec 거부지만 잡힘. - **Unsigned URL upload**: 사용자가 임의 path. - **`` 만 = 안전 가정**: HTTPS + 정책 같이. ## 🤖 LLM 활용 힌트 - A01-A03 자동 검증 (lint + 테스트). - helmet + CORS + rate-limit + zod 4종 어디나. - 매 API 에 (1) 인증 (2) 권한 (3) 입력 검증 (4) 출력 sanitize. ## 🔗 관련 문서 - [[Security_2FA_TOTP_WebAuthn]] - [[Security_OAuth_Flows]] - [[Security_CSP_Headers]] - [[Security_Auth_Authz_Patterns]]