API와 관련된 가장 중요한 10가지 보안 위험과 이를 완화하는 방법에 대해 설명합니다.
OWASP(오픈 월드와이드 애플리케이션 보안 프로젝트)에서 발표한 API별 보안 위험 순위입니다. 2023년판은 API의 광범위한 채택으로 인해 증가한 위협을 반영한 최신 목록을 제공합니다.
객체 ID를 조작하여 다른 사용자의 데이터에 무단으로 액세스할 수 있는 취약점입니다. 가장 빈번하게 발생하고 악용하기 가장 쉬운 취약점으로 알려져 있습니다.
공격자는 URL 또는 요청 매개변수의 ID를 변경하는 것만으로 다른 사용자의 개인 정보, 결제 정보, 주문 내역 등에 액세스할 수 있습니다.
// ID를 수신하고 데이터를 직접 반환 - 권한 확인 없음 app.get('/api/users/:id/orders', async (req, res) => { const orders = await Order.find({ userId: req.params.id }); res.json(orders); });
// 권한 확인을 위해 인증된 사용자의 ID와 대조 확인 app.get('/api/users/:id/orders', authenticate, async (req, res) => { if (req.user.id !== req.params.id && !req.user.isAdmin) { return res.status(403).json({ error: 'Forbidden' }); } const orders = await Order.find({ userId: req.params.id }); res.json(orders); });
인증 메커니즘 구현 결함으로 인해 공격자가 합법적인 사용자 계정을 장악할 수 있는 취약점입니다.
취약한 비밀번호 정책, 부적절한 토큰 관리, 무차별 암호 대입 방지 기능 부족, 부적절한 세션 관리 등이 있습니다.
const jwt = require('jsonwebtoken'); function authenticate(req, res, next) { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Token required' }); try { // 알고리즘을 명시적으로 지정(alg: none 공격 방지) const decoded = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], issuer: 'your-app', }); req.user = decoded; next(); } catch (err) { res.status(401).json({ error: 'Invalid token' }); } }
API 응답에 포함된 객체의 속성(필드)에 대한 접근 제어가 불충분한 취약점입니다. 이 항목은 과도한 데이터 노출과 대량 할당을 결합한 항목입니다.
// 사용자 객체에서 공개 필드만 반환 function sanitizeUser(user, requesterId) { const publicFields = { id: user.id, name: user.name, avatar: user.avatar }; // 소유자만 추가 필드를 볼 수 있습니다. if (requesterId === user.id) { publicFields.email = user.email; publicFields.phone = user.phone; } return publicFields; // ❌ 반환 사용자; 비밀번호_해시 등이 유출될 수 있습니다. }
API 요청 크기, 빈도 또는 리소스 소비에 제한이 없어 DoS 공격이나 비용 급증으로 이어질 수 있는 취약점입니다.
const rateLimit = require('express-rate-limit'); // 글로벌 요금 제한 app.use(rateLimit({ windowMs: 15 * 60 * 1000, // 15분 max: 100, // 최대 100건의 요청 standardHeaders: true, })); // 페이로드 크기 제한 app.use(express.json({ limit: '10kb' })); // 페이지 매김 상한 app.get('/api/items', (req, res) => { const limit = Math.min(parseInt(req.query.limit) || 20, 100); // ... });
관리자 기능 및 엔드포인트에 대한 적절한 액세스 제어가 구현되지 않아 일반 사용자가 관리 작업을 수행할 수 있는 취약점입니다.
function authorize(...roles) { return (req, res, next) => { if (!roles.includes(req.user.role)) { return res.status(403).json({ error: 'Insufficient permissions' }); } next(); }; } // 관리자 전용 액세스 app.delete('/api/admin/users/:id', authenticate, authorize('admin'), deleteUser); // 관리자 및 진행자 app.put('/api/posts/:id/moderate', authenticate, authorize('admin', 'moderator'), moderatePost);
중요한 비즈니스 로직 흐름이 자동화된 공격으로부터 보호되지 않는 취약점입니다. 예: 티켓 스캘핑, 대량 쿠폰 수집, 스팸 게시 등
서버가 유효성 검사 없이 사용자가 제공한 URL을 가져와 내부 네트워크에 무단으로 액세스할 수 있는 취약점입니다.
const { URL } = require('url'); function isAllowedUrl(input) { try { const url = new URL(input); // 프로토콜 제한 if (!['https:', 'http:'].includes(url.protocol)) return false; // 사설 IP 범위 차단 const blocked = ['127.0.0.1', 'localhost', '0.0.0.0', '169.254.169.254']; if (blocked.includes(url.hostname)) return false; // 내부 네트워크 범위 확인(10.x, 172.16-31.x, 192.168.x) if (/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(url.hostname)) return false; return true; } catch { return false; } }
API 서버 또는 프레임워크의 부적절한 보안 설정. 불필요한 HTTP 메서드 허용, 지나치게 허용적인 CORS 정책, 디버그 정보 노출 등이 포함됩니다.
const helmet = require('helmet'); const cors = require('cors'); app.use(helmet()); app.use(cors({ origin: ['https://app.example.com'], // 와일드카드 사용 금지 methods: ['GET', 'POST', 'PUT', 'DELETE'], credentials: true, })); // 오류 처리기: 프로덕션에서 스택 추적을 반환하지 않음 app.use((err, req, res, next) => { res.status(500).json({ error: process.env.NODE_ENV === 'production' ? 'Internal Server Error' : err.message, }); });
이전 API 엔드포인트 버전이 보안 패치가 적용되지 않은 채 노출된 상태입니다. 섀도 API 또는 좀비 API라고도 합니다.
타사 API의 응답을 검증 없이 신뢰하는 취약점입니다. 외부 API가 손상되면 그 영향이 자체 시스템으로 전파됩니다.
const Ajv = require('ajv'); const ajv = new Ajv(); // 스키마로 외부 API 응답 유효성 검사하기 const externalSchema = { type: 'object', required: ['id', 'status'], properties: { id: { type: 'string', maxLength: 50 }, status: { type: 'string', enum: ['active', 'inactive'] }, }, additionalProperties: false, }; const validate = ajv.compile(externalSchema); async function fetchExternalData() { const res = await fetch('https://api.external.com/data'); const data = await res.json(); if (!validate(data)) { throw new Error('External API response validation failed'); } return data; }
| ID | 취약성 | 심각도 | 주요 완화 |
|---|---|---|---|
| API1 | Broken Object Level Authorization | Critical | 개체 수준 권한 확인 |
| API2 | Broken Authentication | Critical | 적절한 토큰 관리 및 무차별 암호 대입 보호 |
| API3 | Broken Object Property Level Authorization | High | 명시적 응답 필드 필터링 |
| API4 | Unrestricted Resource Consumption | High | 속도 제한 및 페이로드 크기 제한 |
| API5 | Broken Function Level Authorization | High | 역할 기반 액세스 제어 |
| API6 | Unrestricted Access to Sensitive Business Flows | Medium | 캡차 및 비즈니스 수준 요금 제한 |
| API7 | Server Side Request Forgery | High | URL 유효성 검사 및 비공개 IP 차단 |
| API8 | Security Misconfiguration | Medium | 보안 헤더 및 CORS 구성 |
| API9 | Improper Inventory Management | Medium | API 인벤토리 관리 및 사용 중단 정책 |
| API10 | Unsafe Consumption of APIs | Medium | 외부 API 응답의 스키마 유효성 검사 |