API Security Guidelines
Table of Contents
- Authentication
- Authorization
- Input Validation
- Data Protection
- Rate Limiting
- HTTPS & TLS
- CORS
- Security Headers
- API Keys
- Vulnerability Management
- Security Testing
- Incident Response
Authentication
OAuth 2.0 with OpenID Connect
- Use OpenID Connect for authentication
- Implement the Authorization Code flow for web applications
- Use PKCE (Proof Key for Code Exchange) for public clients
- Set appropriate token expiration times:
- Access tokens: 15-60 minutes
- Refresh tokens: 7-30 days
- ID tokens: 5 minutes
JWT Best Practices
- Use strong signing algorithms (RS256, ES256)
- Set reasonable expiration times
- Include standard claims (iss, sub, aud, exp, iat, nbf)
- Validate all tokens on the server side
- Store tokens securely (httpOnly, Secure, SameSite=Strict)
Example JWT Validation
const jwt = require('jsonwebtoken');
function validateToken(token) {
return new Promise((resolve, reject) => {
jwt.verify(
token,
process.env.PUBLIC_KEY,
{
algorithms: ['RS256'],
issuer: 'https://auth.dexaminds.com',
audience: 'api.dexaminds.com',
clockTolerance: 30, // seconds
maxAge: '1h',
},
(err, decoded) => {
if (err) return reject(err);
resolve(decoded);
}
);
});
}
Authorization
Role-Based Access Control (RBAC)
- Define clear roles and permissions
- Implement attribute-based access control (ABAC) for fine-grained control
- Use policy-based authorization for complex rules
- Implement resource-level permissions
Example Authorization Middleware
function requirePermission(permission) {
return (req, res, next) => {
if (!req.user || !req.user.permissions.includes(permission)) {
return res.status(403).json({
error: {
code: 'FORBIDDEN',
message: 'Insufficient permissions',
details: [{
code: 'MISSING_PERMISSION',
target: permission,
message: `Requires '${permission}' permission`
}]
}
});
}
next();
};
}
Validation Rules
- Validate all input data (headers, query params, request body, path params)
- Use strong input validation libraries (Joi, class-validator, etc.)
- Implement allow-listing (not block-listing)
- Sanitize all output data
- Set maximum lengths for all string inputs
Example Validation with Joi
const userSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string()
.min(12)
.max(72) // bcrypt max length
.pattern(/[a-z]/)
.pattern(/[A-Z]/)
.pattern(/[0-9]/)
.pattern(/[^a-zA-Z0-9]/)
.required(),
role: Joi.string().valid('user', 'admin', 'support').default('user'),
metadata: Joi.object({
termsAccepted: Joi.boolean().valid(true).required(),
marketingOptIn: Joi.boolean().default(false)
}).required()
});
Data Protection
Data Encryption
- Encrypt sensitive data at rest (AES-256)
- Use TLS 1.2+ for data in transit
- Hash passwords with strong algorithms (Argon2, bcrypt, PBKDF2)
- Encrypt sensitive fields in the database
Example Password Hashing
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12;
async function hashPassword(password) {
return bcrypt.hash(password, SALT_ROUNDS);
}
async function verifyPassword(password, hash) {
return bcrypt.compare(password, hash);
}
Rate Limiting
Implementation
- Implement rate limiting by IP, user, and API key
- Use sliding window or token bucket algorithm
- Set appropriate limits based on endpoint sensitivity
- Include rate limit headers in responses
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 75
X-RateLimit-Reset: 1625040000
Retry-After: 60
HTTPS & TLS
Best Practices
- Use TLS 1.2 or higher
- Implement HSTS with preload
- Disable weak ciphers and protocols
- Use strong key exchange (ECDHE, DHE)
- Rotate certificates regularly (90 days or less)
Recommended Cipher Suites
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
CORS
Configuration
- Be specific with allowed origins
- Don't use wildcard (*) for credentials
- Set appropriate headers
- Preflight caching
Access-Control-Allow-Origin: https://app.dexaminds.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com; img-src 'self' data:; style-src 'self' 'unsafe-inline';
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
API Keys
Best Practices
- Don't use API keys for user authentication
- Rotate keys regularly
- Set appropriate permissions and restrictions
- Monitor and audit key usage
- Implement key revocation
Example API Key Middleware
async function validateApiKey(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({
error: {
code: 'UNAUTHORIZED',
message: 'API key is required'
}
});
}
try {
const keyInfo = await db.apiKeys.findByKey(apiKey);
if (!keyInfo || keyInfo.revoked) {
throw new Error('Invalid API key');
}
// Check rate limits
if (await isRateLimited(keyInfo.id)) {
return res.status(429).json({
error: {
code: 'RATE_LIMIT_EXCEEDED',
message: 'Too many requests'
}
});
}
// Attach key info to request
req.apiKey = keyInfo;
next();
} catch (error) {
return res.status(401).json({
error: {
code: 'INVALID_API_KEY',
message: 'Invalid or revoked API key'
}
});
}
}
Vulnerability Management
Practices
- Keep dependencies updated
- Use dependency vulnerability scanning (npm audit, Snyk, Dependabot)
- Perform regular security audits
- Implement a bug bounty program
- Follow responsible disclosure policies
Example .npmrc for Secure Installs
# Prevent executing arbitrary code during package install
ignore-scripts=true
# Always use HTTPS for registry
registry=https://registry.npmjs.org/
# Use strict SSL
strict-ssl=true
# Use package-lock.json
package-lock=true
Security Testing
Testing Types
- Static Application Security Testing (SAST)
- Dynamic Application Security Testing (DAST)
- Software Composition Analysis (SCA)
- Penetration testing
- Security code reviews
OWASP Top 10 Coverage
- Injection: Use parameterized queries, ORMs
- Broken Authentication: Implement proper auth flows, MFA
- Sensitive Data Exposure: Encrypt data, use secure protocols
- XML External Entities (XXE): Disable XML external entities
- Broken Access Control: Implement proper authorization checks
- Security Misconfiguration: Harden configurations, disable debug modes
- Cross-Site Scripting (XSS): Output encoding, Content Security Policy
- Insecure Deserialization: Validate input, use safe serializers
- Using Components with Known Vulnerabilities: Regular updates, SCA tools
- Insufficient Logging & Monitoring: Comprehensive logging, alerting
Incident Response
Response Plan
- Preparation: Document procedures, assign roles
- Detection & Analysis: Monitor logs, detect anomalies
- Containment: Isolate affected systems
- Eradication: Remove threat, patch vulnerabilities
- Recovery: Restore services, verify integrity
- Post-Incident: Document lessons, update policies
# Prevent MIME type sniffing
X-Content-Type-Options: nosniff
# Prevent clickjacking
X-Frame-Options: DENY
# Enable XSS filter
X-XSS-Protection: 1; mode=block
# Content Security Policy
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;
# Prevent iframe embedding
X-Permitted-Cross-Domain-Policies: none
# Disable caching for sensitive endpoints
Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache
Expires: 0
Secure Development Lifecycle (SDL)
Phases
- Training: Security awareness for developers
- Requirements: Define security requirements
- Design: Threat modeling, security architecture
- Implementation: Secure coding practices
- Verification: Code review, security testing
- Release: Security review, sign-off
- Response: Incident response plan
Security Checklist for Code Review
- [ ] Input validation and output encoding
- [ ] Authentication and session management
- [ ] Authorization checks
- [ ] Secure configuration
- [ ] Cryptography usage
- [ ] Error handling and logging
- [ ] Data protection
- [ ] Communication security
- [ ] File operations
- [ ] Memory management
Nginx
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' cdn.example.com; style-src 'self' 'unsafe-inline';";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";
Apache
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "DENY"
Header set X-XSS-Protection "1; mode=block"
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' cdn.example.com; style-src 'self' 'unsafe-inline';"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
Express.js
const helmet = require('helmet');
app.use(helmet());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy({ policy: 'same-origin' }));
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"],
imgSrc: ["'self'"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
})
);
Secure API Design Principles
Principles
- Least Privilege: Grant minimum necessary permissions
- Defense in Depth: Multiple layers of security
- Fail Securely: Default deny, safe error messages
- Keep It Simple: Reduce attack surface
- Secure by Default: Secure configurations out of the box
- Separation of Duties: Divide responsibilities
- Audit Trails: Comprehensive logging
- Privacy by Design: Data protection from the start
- Keep Security Up-to-Date: Regular updates and patches
- Assume Breach: Plan for security incidents