stack-analysis
CHAPTER 103 / 120
읽기 약 2분
FUNCTION
OWASP Top 10 실전 방어 코드
핵심 개념
2024 Top 10·각 항목 방어 패턴·실전 코드 — 웹 보안 표준.
본문
OWASP Top 10 (2024)
A01: Broken Access Control
A02: Cryptographic Failures
A03: Injection
A04: Insecure Design
A05: Security Misconfiguration
A06: Vulnerable Components
A07: Identification & Auth Failures
A08: Software & Data Integrity
A09: Logging & Monitoring Failures
A10: SSRF (Server-Side Request Forgery)A01: Broken Access Control
// ❌ 권한 검증 누락
app.get('/api/orders/:id', authenticate, async (req, res) => {
const order = await db.order.findUnique({ where: { id: req.params.id } });
res.json(order); // 누구나 모든 주문 조회 가능
});
// ✅ 소유자 검증
app.get('/api/orders/:id', authenticate, async (req, res) => {
const order = await db.order.findUnique({ where: { id: req.params.id } });
if (!order) return res.status(404).end();
if (order.userId !== req.user!.id && req.user!.role !== 'admin') {
return res.status(403).end();
}
res.json(order);
});
// ✅ 자동 — Prisma RLS 또는 도메인 메서드
async function getOrderForUser(orderId: string, userId: string) {
return db.order.findFirst({
where: { id: orderId, userId }, // 자동 필터
});
}A02: Cryptographic Failures
// ❌ 평문 저장
await db.user.create({ data: { email, password } });
// ✅ bcrypt
import bcrypt from 'bcryptjs';
const hash = await bcrypt.hash(password, 12);
await db.user.create({ data: { email, passwordHash: hash } });
// 검증
const ok = await bcrypt.compare(password, user.passwordHash);
// ❌ 약한 암호화
const cipher = crypto.createCipher('aes-128-cbc', secret);
// ✅ 강한 알고리즘
import crypto from 'crypto';
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);A03: Injection (SQL/NoSQL/Command)
// ❌ SQL Injection
const sql = `SELECT * FROM users WHERE email = '${email}'`;
// ✅ Parameterized (Prisma 자동)
const user = await db.user.findUnique({ where: { email } });
// 또는 raw에서도 parameterized
await db.$queryRaw`SELECT * FROM users WHERE email = ${email}`;
// ❌ Command Injection
const { exec } = require('child_process');
exec(`convert ${userInput} output.jpg`);
// ✅ execFile + 인자 분리
const { execFile } = require('child_process');
execFile('convert', [userInput, 'output.jpg']);
// ❌ NoSQL Injection
db.users.findOne({ email: req.body.email });
// req.body.email = { $ne: null } → 모든 사용자 매치
// ✅ 검증
const { email } = z.object({ email: z.string().email() }).parse(req.body);A04: Insecure Design (비즈니스 로직)
// ❌ 잔액 검증 없이 차감
async function withdraw(userId: string, amount: number) {
await db.user.update({
where: { id: userId },
data: { balance: { decrement: amount } },
});
}
// ✅ 트랜잭션 + 검증
async function withdraw(userId: string, amount: number) {
return db.$transaction(async (tx) => {
const user = await tx.user.findUnique({ where: { id: userId } });
if (!user || user.balance < amount) {
throw new Error('Insufficient funds');
}
return tx.user.update({
where: { id: userId },
data: { balance: { decrement: amount } },
});
}, { isolationLevel: 'Serializable' });
}
// ✅ DB 제약 (CHECK)
ALTER TABLE users ADD CONSTRAINT balance_non_negative CHECK (balance >= 0);A05: Security Misconfiguration
// ✅ 프로덕션 체크리스트
- 디폴트 비밀번호 변경
- 디버그 모드 비활성 (NODE_ENV=production)
- 에러에 stack trace 노출 X
- 디렉터리 listing 차단
- helmet 미들웨어
- CORS 화이트리스트
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: { /* ... */ },
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
}));A06: Vulnerable Components
# 자동 취약점 검사
pnpm audit
pnpm audit --fix
# CI 통합
pnpm dlx better-npm-audit audit --level moderate
# Snyk (deeper)
pnpm dlx snyk test
pnpm dlx snyk monitor
# Renovate / Dependabot (자동 PR)
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule: { interval: weekly }
open-pull-requests-limit: 10A07: Auth Failures
// ❌ 무차별 로그인
app.post('/login', async (req, res) => {
// 무한 시도 가능
});
// ✅ Rate limit + 잠금
import rateLimit from 'express-rate-limit';
const loginLimit = rateLimit({
windowMs: 60 * 1000,
max: 5,
skipSuccessfulRequests: true,
});
app.post('/login', loginLimit, async (req, res) => {
// 5회 실패 시 계정 잠금
const attempts = await redis.incr(`login_attempts:${req.body.email}`);
if (attempts === 1) await redis.expire(`login_attempts:${req.body.email}`, 900);
if (attempts > 10) {
return res.status(429).json({ error: 'account_locked', unlockIn: 900 });
}
// ...
});
// ✅ 비밀번호 정책
const PasswordSchema = z.string()
.min(10)
.regex(/[A-Z]/, '대문자 포함')
.regex(/[a-z]/, '소문자 포함')
.regex(/\d/, '숫자 포함')
.regex(/[!@#$%^&*]/, '특수문자 포함');A08: Software & Data Integrity
// ✅ Subresource Integrity (SRI)
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>
// ✅ Webhook 서명 검증
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET)
.update(body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
throw new Error('Invalid signature');
}
// ✅ Dependency lockfile commit (pnpm-lock.yaml)
// → 매번 같은 버전 보장A09: Logging & Monitoring
// ✅ 구조화 로그 (실패한 인증·권한 위반)
logger.warn({
event: 'auth.failure',
userId,
ip: req.ip,
userAgent: req.headers['user-agent'],
reason: 'invalid_password',
}, 'Login failed');
// ✅ 알림
if (failedAttempts > 5) {
await alertOps({
type: 'security',
severity: 'high',
message: `${failedAttempts} failed logins for ${email}`,
});
}
// ✅ 보존 정책
// - 로그: 90일~1년
// - 보안 이벤트: 3년A10: SSRF
// ❌ 사용자 URL 직접 fetch
app.get('/proxy', async (req, res) => {
const data = await fetch(req.query.url); // 내부 IP 접근 가능
});
// ✅ URL 검증 + 화이트리스트
const ALLOWED_HOSTS = ['api.example.com', 'cdn.example.com'];
app.get('/proxy', async (req, res) => {
const url = new URL(req.query.url as string);
if (!ALLOWED_HOSTS.includes(url.hostname)) {
return res.status(400).end();
}
// 내부 IP 차단
if (
url.hostname === 'localhost' ||
url.hostname.startsWith('127.') ||
url.hostname.startsWith('169.254.') || // AWS metadata
url.hostname.startsWith('10.') ||
url.hostname.startsWith('192.168.')
) {
return res.status(400).end();
}
const data = await fetch(url);
res.json(await data.json());
});다음 챕터
CH.104 "CSP(Content Security Policy) 완벽 설정".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 코드의 OWASP 보안 부분을 분석해서 실전 분석 + 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
OWASP 보안 관련 베스트 프랙티스 5가지를 비교 분석해서 패턴 추출를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 프로젝트 전체에서 OWASP 보안 최적화 가능 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 시장의 OWASP 보안 트렌드를 솔직히 알려줘.
⭐ 이것만 기억하세요
OWASP Top 10 실전 방어 코드는 이 3가지만 확실히 잡으세요
1.OWASP Top 10 = 90% 공격 방어 — 1개씩 체크
2.Prisma + Zod = SQL injection + 입력 검증 자동
3.helmet + CSP + Rate Limit = 미들웨어 3종 필수
공유하기
진행도 103 / 120