topic난이도 · 약 25

OWASP Top 10 2025 — AI 시대 웹 취약점 10선

권한 깨짐 · 암호 실패 · 인젝션 · 부실 설계 · SSRF · 의존성 — 가장 자주 터지는 10가지.

#OWASP#SQLi#XSS#SSRF#CSP#Rate Limit#npm audit
왜 배우는가

AI가 만든 코드에 이 10가지 패턴이 들어있으면 프로덕션 배포 직후 털린다. 각 항목을 '내 코드에서 어디서 발생 가능한가'로 치환해 읽는다.

OWASP Top 10은 전 세계 웹 보안 단골 취약점 랭킹. 버전마다 순위가 바뀌지만 본질은 같다. 아래는 2021 공식 + 2025 추세를 통합한 바이브코더 관점 정리.

#취약점근본 원인바이브코더가 할 일
A01Broken Access Control (IDOR 등)인가 누락서버/DB에서 소유권·역할 체크 (ch24-1)
A02Cryptographic Failures약한 암호, 평문 저장TLS 1.2+, bcrypt/argon2, AES-GCM
A03Injection (SQL·NoSQL·Cmd)사용자 입력을 쿼리/명령에 직접 삽입Prepared Statement, ORM, 입력 검증
A04Insecure Design설계 단계 결함위협 모델링, 테스트 부정 케이스
A05Security Misconfiguration기본 비번, 노출 에러, 열린 포트CSP 헤더, 최소 권한, Error 핸들링
A06Vulnerable Components오래된 라이브러리`npm audit`, Dependabot
A07Auth Failures약한 비번, 무제한 시도MFA, Rate Limit, bcrypt
A08Software/Data Integrity서명 없는 업데이트, CI 공격SBOM, 서명 검증
A09Logging/Monitoring Failures침입 모름구조화 로그, 알림 (ch09-5 관측성)
A10SSRF서버가 내부 URL 요청하게 유도URL 화이트리스트, 메타데이터 차단

2025년대 추가 테마: LLM 보안. OWASP LLM Top 10 별도 발간. 프롬프트 인젝션 (악성 입력이 시스템 프롬프트 오염), 민감 정보 유출 (LLM에 비밀 데이터 전달), 모델 공급망 공격. Claude Code 쓰는 바이브코더에게 실제로 직결.

typescript
// ━━━ A01 방어: 소유권 체크 ━━━
app.get("/api/posts/:id/edit", async (req, res) => {
  const post = await db.posts.find(req.params.id);
  if (!post) return res.sendStatus(404);
  if (post.authorId !== req.user.id) return res.sendStatus(403);  // ← 인가
  // 편집 로직
});

// ━━━ A03 방어: Prepared Statement / ORM ━━━
// ❌ SQL Injection
const user = await db.query(`SELECT * FROM users WHERE email='${email}'`);
// email = "' OR 1=1--"  →  전체 유저 노출

// ✅ 매개변수 바인딩
const user = await db.query("SELECT * FROM users WHERE email = $1", [email]);

// ━━━ A03 방어: Shell 명령 인젝션 ━━━
// ❌ exec("convert " + filename + " out.png")  → 공백·; 들어오면 임의 명령
// ✅ execFile("convert", [filename, "out.png"])  → 인자 배열로

// ━━━ A05 방어: CSP 헤더 (XSS 2차 방어선) ━━━
app.use((req, res, next) => {
  res.setHeader("Content-Security-Policy",
    "default-src 'self'; script-src 'self'; object-src 'none'");
  res.setHeader("X-Content-Type-Options", "nosniff");
  res.setHeader("X-Frame-Options", "DENY");
  res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
  next();
});

// ━━━ A07 방어: 로그인 Rate Limit ━━━
import rateLimit from "express-rate-limit";
app.use("/api/login", rateLimit({
  windowMs: 15 * 60_000,   // 15분
  max: 5,                   // IP당 5회
  message: "Too many attempts",
}));

// ━━━ A10 방어: SSRF ━━━
// 사용자가 입력한 URL을 서버가 fetch할 때
const allowed = ["https://images.jit.co.kr", "https://cdn.jit.co.kr"];
const parsed = new URL(userUrl);
if (!allowed.includes(parsed.origin)) throw new Error("blocked");
// 추가: AWS 메타데이터(169.254.169.254) 차단, 사설 IP 차단

Claude에게 "이 코드에 OWASP Top 10 관점 취약점 있어?" 물어보면 꽤 잘 찾는다. 정기적으로 코드 리뷰 시 습관화.

의존성 감사 루틴. 주 1회: `npm audit` → High/Critical 즉시 업데이트. GitHub Dependabot 활성화. 중요 프로젝트는 SBOM(Software Bill of Materials) 생성 — `npm ls --all` 또는 `syft`.

실전 최소 보안 체크리스트 10. ① TLS 1.2+ / HSTS ② HttpOnly+Secure+SameSite 쿠키 ③ bcrypt/argon2 ④ Prepared Statement 또는 ORM ⑤ 서버측 인가 체크 ⑥ CSP·XFO·XCTO 헤더 ⑦ Rate Limit (로그인·API) ⑧ `.env` Git 제외 ⑨ 로그·알림 ⑩ `npm audit` 주간.

실기 드릴 3문항
edit실기 드릴 · 단답형

사용자가 입력한 URL을 서버가 fetch하게 유도해 내부 시스템을 탐색하는 공격 이름은?

edit실기 드릴 · 단답형

SQL Injection을 근본적으로 막는 표준 기법은?

check_circle실기 드릴 · OX

`npm audit`을 한 번만 돌리면 보안 의존성 관리가 끝난다.