API Security Best Practices 🐧
Overview
Guide developers in building secure APIs by implementing authentication, authorization, input validation, rate limiting, and protection against common vulnerabilities. This skill covers security patterns for REST, GraphQL, and WebSocket APIs.
When to Use This Skill
- - Use when designing new API endpoints
- Use when securing existing APIs
- Use when implementing authentication and authorization
- Use when protecting against API attacks (injection, DDoS, etc.)
- Use when conducting API security reviews
- Use when preparing for security audits
- Use when implementing rate limiting and throttling
- Use when handling sensitive data in APIs
How It Works
Step 1: Authentication & Authorization
I'll help you implement secure authentication:
- - Choose authentication method (JWT, OAuth 2.0, API keys)
- Implement token-based authentication
- Set up role-based access control (RBAC)
- Secure session management
- Implement multi-factor authentication (MFA)
Step 2: Input Validation & Sanitization
Protect against injection attacks:
- - Validate all input data
- Sanitize user inputs
- Use parameterized queries
- Implement request schema validation
- Prevent SQL injection, XSS, and command injection
Step 3: Rate Limiting & Throttling
Prevent abuse and DDoS attacks:
- - Implement rate limiting per user/IP
- Set up API throttling
- Configure request quotas
- Handle rate limit errors gracefully
- Monitor for suspicious activity
Step 4: Data Protection
Secure sensitive data:
- - Encrypt data in transit (HTTPS/TLS)
- Encrypt sensitive data at rest
- Implement proper error handling (no data leaks)
- Sanitize error messages
- Use secure headers
Step 5: API Security Testing
Verify security implementation:
- - Test authentication and authorization
- Perform penetration testing
- Check for common vulnerabilities (OWASP API Top 10)
- Validate input handling
- Test rate limiting
Examples
Example 1: Implementing JWT Authentication
CODEBLOCK0
Example 2: Input Validation and SQL Injection Prevention
CODEBLOCK1
Example 3: Rate Limiting and DDoS Protection
CODEBLOCK2
Best Practices
✅ Do This
- - Use HTTPS Everywhere - Never send sensitive data over HTTP
- Implement Authentication - Require authentication for protected endpoints
- Validate All Inputs - Never trust user input
- Use Parameterized Queries - Prevent SQL injection
- Implement Rate Limiting - Protect against brute force and DDoS
- Hash Passwords - Use bcrypt with salt rounds >= 10
- Use Short-Lived Tokens - JWT access tokens should expire quickly
- Implement CORS Properly - Only allow trusted origins
- Log Security Events - Monitor for suspicious activity
- Keep Dependencies Updated - Regularly update packages
- Use Security Headers - Implement Helmet.js
- Sanitize Error Messages - Don't leak sensitive information
❌ Don't Do This
- - Don't Store Passwords in Plain Text - Always hash passwords
- Don't Use Weak Secrets - Use strong, random JWT secrets
- Don't Trust User Input - Always validate and sanitize
- Don't Expose Stack Traces - Hide error details in production
- Don't Use String Concatenation for SQL - Use parameterized queries
- Don't Store Sensitive Data in JWT - JWTs are not encrypted
- Don't Ignore Security Updates - Update dependencies regularly
- Don't Use Default Credentials - Change all default passwords
- Don't Disable CORS Completely - Configure it properly instead
- Don't Log Sensitive Data - Sanitize logs
Common Pitfalls
Problem: JWT Secret Exposed in Code
Symptoms: JWT secret hardcoded or committed to Git
Solution:
\
\\
javascript
// ❌ Bad
const JWT_SECRET = 'my-secret-key';
// ✅ Good
const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET) {
throw new Error('JWT_SECRET environment variable is required');
}
// Generate strong secret
// node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
\\ INLINECODE2
Problem: Weak Password Requirements
Symptoms: Users can set weak passwords like "password123"
Solution:
\
\\
javascript
const passwordSchema = z.string()
.min(12, 'Password must be at least 12 characters')
.regex(/[A-Z]/, 'Must contain uppercase letter')
.regex(/[a-z]/, 'Must contain lowercase letter')
.regex(/[0-9]/, 'Must contain number')
.regex(/[^A-Za-z0-9]/, 'Must contain special character');
// Or use a password strength library
const zxcvbn = require('zxcvbn');
const result = zxcvbn(password);
if (result.score < 3) {
return res.status(400).json({
error: 'Password too weak',
suggestions: result.feedback.suggestions
});
}
\\ INLINECODE5
Problem: Missing Authorization Checks
Symptoms: Users can access resources they shouldn't
Solution:
\
\\
javascript
// ❌ Bad: Only checks authentication
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
await prisma.post.delete({ where: { id: req.params.id } });
res.json({ success: true });
});
// ✅ Good: Checks both authentication and authorization
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
const post = await prisma.post.findUnique({
where: { id: req.params.id }
});
if (!post) {
return res.status(404).json({ error: 'Post not found' });
}
// Check if user owns the post or is admin
if (post.userId !== req.user.userId && req.user.role !== 'admin') {
return res.status(403).json({
error: 'Not authorized to delete this post'
});
}
await prisma.post.delete({ where: { id: req.params.id } });
res.json({ success: true });
});
\\ INLINECODE8
Problem: Verbose Error Messages
Symptoms: Error messages reveal system details
Solution:
\
\\
javascript
// ❌ Bad: Exposes database details
app.post('/api/users', async (req, res) => {
try {
const user = await prisma.user.create({ data: req.body });
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
// Error: "Unique constraint failed on the fields: (email
)"
}
});
// ✅ Good: Generic error message
app.post('/api/users', async (req, res) => {
try {
const user = await prisma.user.create({ data: req.body });
res.json(user);
} catch (error) {
console.error('User creation error:', error); // Log full error
if (error.code === 'P2002') {
return res.status(400).json({
error: 'Email already exists'
});
}
res.status(500).json({
error: 'An error occurred while creating user'
});
}
});
\\ INLINECODE12
Security Checklist
Authentication & Authorization
- - [ ] Implement strong authentication (JWT, OAuth 2.0)
- [ ] Use HTTPS for all endpoints
- [ ] Hash passwords with bcrypt (salt rounds >= 10)
- [ ] Implement token expiration
- [ ] Add refresh token mechanism
- [ ] Verify user authorization for each request
- [ ] Implement role-based access control (RBAC)
Input Validation
- - [ ] Validate all user inputs
- [ ] Use parameterized queries or ORM
- [ ] Sanitize HTML content
- [ ] Validate file uploads
- [ ] Implement request schema validation
- [ ] Use allowlists, not blocklists
Rate Limiting & DDoS Protection
- - [ ] Implement rate limiting per user/IP
- [ ] Add stricter limits for auth endpoints
- [ ] Use Redis for distributed rate limiting
- [ ] Return proper rate limit headers
- [ ] Implement request throttling
Data Protection
- - [ ] Use HTTPS/TLS for all traffic
- [ ] Encrypt sensitive data at rest
- [ ] Don't store sensitive data in JWT
- [ ] Sanitize error messages
- [ ] Implement proper CORS configuration
- [ ] Use security headers (Helmet.js)
Monitoring & Logging
- - [ ] Log security events
- [ ] Monitor for suspicious activity
- [ ] Set up alerts for failed auth attempts
- [ ] Track API usage patterns
- [ ] Don't log sensitive data
OWASP API Security Top 10
- 1. Broken Object Level Authorization - Always verify user can access resource
- Broken Authentication - Implement strong authentication mechanisms
- Broken Object Property Level Authorization - Validate which properties user can access
- Unrestricted Resource Consumption - Implement rate limiting and quotas
- Broken Function Level Authorization - Verify user role for each function
- Unrestricted Access to Sensitive Business Flows - Protect critical workflows
- Server Side Request Forgery (SSRF) - Validate and sanitize URLs
- Security Misconfiguration - Use security best practices and headers
- Improper Inventory Management - Document and secure all API endpoints
- Unsafe Consumption of APIs - Validate data from third-party APIs
Related Skills
- -
@ethical-hacking-methodology - Security testing perspective - INLINECODE14 - Testing for SQL injection
- INLINECODE15 - Testing for XSS vulnerabilities
- INLINECODE16 - Authentication vulnerabilities
- INLINECODE17 - Backend development standards
- INLINECODE18 - Debug security issues
Additional Resources
Pro Tip: Security is not a one-time task - regularly audit your APIs, keep dependencies updated, and stay informed about new vulnerabilities!
🐧 Built by 무펭이 — 무펭이즘(Mupengism) 생태계 스킬
API 보안 모범 사례 🐧
개요
인증, 권한 부여, 입력 검증, 속도 제한 및 일반적인 취약점에 대한 보호를 구현하여 개발자가 안전한 API를 구축할 수 있도록 안내합니다. 이 스킬은 REST, GraphQL 및 WebSocket API에 대한 보안 패턴을 다룹니다.
이 스킬을 사용해야 할 때
- - 새로운 API 엔드포인트를 설계할 때
- 기존 API를 보호할 때
- 인증 및 권한 부여를 구현할 때
- API 공격(인젝션, DDoS 등)으로부터 보호할 때
- API 보안 검토를 수행할 때
- 보안 감사를 준비할 때
- 속도 제한 및 스로틀링을 구현할 때
- API에서 민감한 데이터를 처리할 때
작동 방식
1단계: 인증 및 권한 부여
안전한 인증 구현을 도와드립니다:
- - 인증 방법 선택 (JWT, OAuth 2.0, API 키)
- 토큰 기반 인증 구현
- 역할 기반 접근 제어(RBAC) 설정
- 안전한 세션 관리
- 다중 인증(MFA) 구현
2단계: 입력 검증 및 정화
인젝션 공격으로부터 보호:
- - 모든 입력 데이터 검증
- 사용자 입력 정화
- 매개변수화된 쿼리 사용
- 요청 스키마 검증 구현
- SQL 인젝션, XSS 및 명령 인젝션 방지
3단계: 속도 제한 및 스로틀링
남용 및 DDoS 공격 방지:
- - 사용자/IP별 속도 제한 구현
- API 스로틀링 설정
- 요청 할당량 구성
- 속도 제한 오류 우아하게 처리
- 의심스러운 활동 모니터링
4단계: 데이터 보호
민감한 데이터 보호:
- - 전송 중 데이터 암호화 (HTTPS/TLS)
- 저장된 민감한 데이터 암호화
- 적절한 오류 처리 구현 (데이터 유출 없음)
- 오류 메시지 정화
- 보안 헤더 사용
5단계: API 보안 테스트
보안 구현 확인:
- - 인증 및 권한 부여 테스트
- 침투 테스트 수행
- 일반적인 취약점 확인 (OWASP API Top 10)
- 입력 처리 검증
- 속도 제한 테스트
예제
예제 1: JWT 인증 구현
markdown
안전한 JWT 인증 구현
인증 흐름
- 1. 사용자가 자격 증명으로 로그인
- 서버가 자격 증명 검증
- 서버가 JWT 토큰 생성
- 클라이언트가 토큰을 안전하게 저장
- 클라이언트가 각 요청과 함께 토큰 전송
- 서버가 토큰 검증
구현
1. 안전한 JWT 토큰 생성
\\\javascript
// auth.js
const jwt = require(jsonwebtoken);
const bcrypt = require(bcrypt);
// 로그인 엔드포인트
app.post(/api/auth/login, async (req, res) => {
try {
const { email, password } = req.body;
// 입력 검증
if (!email || !password) {
return res.status(400).json({
error: 이메일과 비밀번호가 필요합니다
});
}
// 사용자 찾기
const user = await db.user.findUnique({
where: { email }
});
if (!user) {
// 사용자 존재 여부를 노출하지 않음
return res.status(401).json({
error: 잘못된 자격 증명입니다
});
}
// 비밀번호 확인
const validPassword = await bcrypt.compare(
password,
user.passwordHash
);
if (!validPassword) {
return res.status(401).json({
error: 잘못된 자격 증명입니다
});
}
// JWT 토큰 생성
const token = jwt.sign(
{
userId: user.id,
email: user.email,
role: user.role
},
process.env.JWT_SECRET,
{
expiresIn: 1h,
issuer: your-app,
audience: your-app-users
}
);
// 리프레시 토큰 생성
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.JWTREFRESHSECRET,
{ expiresIn: 7d }
);
// 데이터베이스에 리프레시 토큰 저장
await db.refreshToken.create({
data: {
token: refreshToken,
userId: user.id,
expiresAt: new Date(Date.now() + 7 24 60 60 1000)
}
});
res.json({
token,
refreshToken,
expiresIn: 3600
});
} catch (error) {
console.error(로그인 오류:, error);
res.status(500).json({
error: 로그인 중 오류가 발생했습니다
});
}
});
\\\
2. JWT 토큰 검증 (미들웨어)
\\\javascript
// middleware/auth.js
const jwt = require(jsonwebtoken);
function authenticateToken(req, res, next) {
// 헤더에서 토큰 가져오기
const authHeader = req.headers[authorization];
const token = authHeader && authHeader.split( )[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
error: 액세스 토큰이 필요합니다
});
}
// 토큰 검증
jwt.verify(
token,
process.env.JWT_SECRET,
{
issuer: your-app,
audience: your-app-users
},
(err, user) => {
if (err) {
if (err.name === TokenExpiredError) {
return res.status(401).json({
error: 토큰이 만료되었습니다
});
}
return res.status(403).json({
error: 유효하지 않은 토큰입니다
});
}
// 요청에 사용자 정보 첨부
req.user = user;
next();
}
);
}
module.exports = { authenticateToken };
\\\
3. 라우트 보호
\\\javascript
const { authenticateToken } = require(./middleware/auth);
// 보호된 라우트
app.get(/api/user/profile, authenticateToken, async (req, res) => {
try {
const user = await db.user.findUnique({
where: { id: req.user.userId },
select: {
id: true,
email: true,
name: true,
// passwordHash는 반환하지 않음
}
});
res.json(user);
} catch (error) {
res.status(500).json({ error: 서버 오류 });
}
});
\\\
4. 토큰 갱신 구현
\\\javascript
app.post(/api/auth/refresh, async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({
error: 리프레시 토큰이 필요합니다
});
}
try {
// 리프레시 토큰 검증
const decoded = jwt.verify(
refreshToken,
process.env.JWTREFRESHSECRET
);
// 데이터베이스에 리프레시 토큰이 있는지 확인
const storedToken = await db.refreshToken.findFirst({
where: {
token: refreshToken,
userId: decoded.userId,
expiresAt: { gt: new Date() }
}
});
if (!storedToken) {
return res.status(403).json({
error: 유효하지 않은 리프레시 토큰입니다
});
}
// 새 액세스 토큰 생성
const user = await db.user.findUnique({
where: { id: decoded.userId }
});
const newToken = jwt.sign(
{