stack-analysis
CHAPTER 36 / 90
읽기 약 2분
FUNCTION
Rate Limiting + CORS + Helmet 실전
핵심 개념
express-rate-limit·CORS 정책·Helmet 헤더 — 프로덕션 보안 기본기.
본문
Helmet — 보안 헤더
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: [`'self'`],
scriptSrc: [`'self'`, `'unsafe-inline'`, 'https://www.googletagmanager.com'],
styleSrc: [`'self'`, `'unsafe-inline'`],
imgSrc: [`'self'`, 'data:', 'https:'],
connectSrc: [`'self'`, 'https://api.example.com'],
},
},
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
frameguard: { action: 'deny' },
}));
// 자동 적용:
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// Strict-Transport-Security
// Referrer-Policy
// Permissions-Policy 등CORS
import cors from 'cors';
const ALLOWED_ORIGINS = [
'https://example.com',
'https://www.example.com',
...(process.env.NODE_ENV === 'development' ? ['http://localhost:3000'] : []),
];
app.use(cors({
origin: (origin, cb) => {
// 모바일 앱·Postman 등 origin 없는 요청 허용
if (!origin) return cb(null, true);
if (ALLOWED_ORIGINS.includes(origin)) return cb(null, true);
return cb(new Error('CORS blocked'));
},
credentials: true, // 쿠키 전송 허용
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count', 'X-Page'],
maxAge: 600, // preflight 캐시 10분
}));Rate Limiting
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
// 글로벌 — IP당 분당 100
const globalLimit = rateLimit({
windowMs: 60 * 1000,
max: 100,
standardHeaders: true, // RateLimit-* 헤더
legacyHeaders: false,
store: new RedisStore({
sendCommand: (...args: string[]) => redisClient.sendCommand(args),
}),
message: { error: 'Too many requests' },
});
app.use(globalLimit);
// 엄격 — 로그인은 IP당 분당 5
const loginLimit = rateLimit({
windowMs: 60 * 1000,
max: 5,
skipSuccessfulRequests: true, // 성공은 카운트 안 함
});
app.post('/auth/login', loginLimit, loginHandler);
// 사용자 기반 (인증 후)
const userLimit = rateLimit({
windowMs: 60 * 1000,
max: 200,
keyGenerator: (req) => req.user?.id ?? req.ip,
});
app.use('/api', authenticate, userLimit);Slow Down (점진적 지연)
import slowDown from 'express-slow-down';
const speedLimit = slowDown({
windowMs: 15 * 60 * 1000, // 15분
delayAfter: 50, // 50회 후 부터
delayMs: (hits) => (hits - 50) * 100, // 100ms씩 증가
maxDelayMs: 5000,
});
app.use(speedLimit);API Key 인증 + 차등 한도
const apiKeyAuth = async (req: Request, res: Response, next: NextFunction) => {
const key = req.headers['x-api-key'] as string;
if (!key) return res.status(401).json({ error: 'No API key' });
const apiKey = await db.apiKey.findUnique({
where: { key: hash(key) },
include: { plan: true },
});
if (!apiKey || apiKey.revokedAt) {
return res.status(401).json({ error: 'Invalid key' });
}
req.apiKey = apiKey;
next();
};
// 플랜별 한도
const planLimit = (req: Request, res: Response, next: NextFunction) => {
const plan = req.apiKey?.plan;
return rateLimit({
windowMs: 60 * 1000,
max: plan?.requestsPerMinute ?? 10,
keyGenerator: () => req.apiKey!.id,
})(req, res, next);
};
app.use('/api', apiKeyAuth, planLimit);비용 폭주 방지 (Cloudflare/AWS WAF)
[1차 방어: Cloudflare]
- DDoS 자동 차단
- IP rate limiting (무료)
- 봇 차단
[2차 방어: 앱 레벨]
- Express rate limit (Redis store)
- API key + 플랜별 한도
[3차 방어: 알림]
- 일일 사용량 임계치 알림
- 계정별 잠금 자동다음 챕터
CH.37 "API 버저닝 전략".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 코드의 보안 미들웨어 부분을 분석해서 실전 분석 + 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
보안 미들웨어 관련 인기 라이브러리/패턴 5개를 비교 분석해서 패턴 추출를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 프로젝트 전체에서 보안 미들웨어 최적화 가능 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 백엔드 시장의 보안 미들웨어 트렌드를 솔직히 알려줘.
⭐ 이것만 기억하세요
Rate Limiting + CORS + Helmet 실전은 이 3가지만 확실히 잡으세요
1.Helmet + CORS + rate-limit = 프로덕션 Express 3대 미들웨어
2.Rate limit는 Redis store — 다중 인스턴스 공유 카운트
3.플랜별 차등 한도 = SaaS 수익화의 기본
공유하기
진행도 36 / 90