---
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.
- **`<input type="password">` 만 = 안전 가정**: 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]]
