5.5 KiB
5.5 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| security-owasp-top-10-practical | OWASP Top 10 — 실전 방어 체크리스트 | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
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 방어)
// ❌
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);
});
-- DB 레벨 추가 방어 (RLS)
CREATE POLICY user_isolation ON orders FOR SELECT USING (user_id = current_setting('app.user_id')::UUID);
A02 — 암호화
// 비밀번호 = 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
// ❌ 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 } });
// Command injection
exec(`convert ${userInput} out.png`); // ❌
execFile('convert', [userInput, 'out.png']); // ✅ shell 안 거침
// 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)
// 로그인 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
// 안 쓰는 endpoint / debug / default password 끄기
app.disable('x-powered-by');
helmet(); // 보안 헤더 자동
// CORS 명시 origin
cors({ origin: ['https://app.com'], credentials: true });
A06 — Vulnerable Components
npm audit
npm audit fix
# 또는 Snyk / Dependabot 자동
A07 — Auth
// 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
// CI 가 lockfile 없이 install 금지
// npm ci --ignore-scripts (postinstall 악성 차단)
// SBOM 생성
// JWT signed + verify with library (직접 X)
A09 — Logging
log.warn('login failed', { email, ip, reason }); // 평문 PW 절대 X
// 보안 이벤트 SIEM 으로
A10 — SSRF
// ❌ 사용자 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 헤더
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.