TL;DR — Application Security Quick Reference
Application security engineering spans design through deployment. Use parameterized queries to prevent SQLi, CSP to prevent XSS, and SameSite cookies with CSRF tokens to prevent CSRF. Hash passwords with bcrypt/Argon2id, sign JWTs with RS256/EdDSA. Store secrets in Vault or cloud secret managers — never in code. Set HSTS, X-Frame-Options, X-Content-Type-Options, and other security headers. Integrate SAST, DAST, dependency scanning, and container scanning in CI/CD for DevSecOps.
Key Takeaways
- Always use parameterized queries — never concatenate user input into SQL.
- Implement CSP headers and output encoding to prevent XSS attacks.
- Authentication (AuthN) and authorization (AuthZ) must be separate, composable layers.
- Use Vault or cloud secret managers for secrets — never commit them to version control.
- Integrate SAST, dependency scanning, and container image scanning in every CI/CD run.
- Enforce rate limiting, input validation, and CORS whitelisting on all API endpoints.
- Encrypt data at rest with AES-256-GCM and data in transit with TLS 1.3.
- Security is a continuous process, not a one-time check — track security debt alongside tech debt.
Application security engineering is more than patching vulnerabilities — it is a systematic approach to embedding security thinking across the entire software development lifecycle. From threat modeling to secure coding, from secrets management to CI/CD security integration, this guide covers everything needed to build secure applications. Whether you are a full-stack developer, backend engineer, or DevOps practitioner, mastering these security practices is essential for protecting user data and business assets.
OWASP Top 10 Overview (2021 Edition)
The OWASP Top 10 is the most widely recognized ranking of web application security risks. The 2021 edition reflects the modern threat landscape with three new categories: Insecure Design, Software and Data Integrity Failures, and SSRF.
| ID | Name | Key Defense |
|---|---|---|
| A01 | Broken Access Control | Least privilege, RBAC, server-side checks |
| A02 | Cryptographic Failures | TLS 1.3, AES-256-GCM, key rotation |
| A03 | Injection | Parameterized queries, input validation, WAF |
| A04 | Insecure Design | Threat modeling, secure design patterns |
| A05 | Security Misconfiguration | Hardened baselines, automated config audits |
| A06 | Vulnerable Components | Dependency scanning, SCA, auto-patching |
| A07 | Auth Failures | MFA, rate limiting, secure sessions |
| A08 | Integrity Failures | Signature verification, secure CI/CD pipelines |
| A09 | Logging Failures | Structured logging, alerting, SIEM |
| A10 | SSRF | URL allowlists, network segmentation |
Authentication & Authorization (AuthN / AuthZ)
Authentication verifies user identity, authorization determines what that user can do. These must be separate layers for flexibility, auditability, and defense in depth.
Password Security
// Password hashing with bcrypt (Node.js)
import bcrypt from 'bcrypt';
const SALT_ROUNDS = 12;
async function hashPassword(plaintext: string): Promise<string> {
return bcrypt.hash(plaintext, SALT_ROUNDS);
}
async function verifyPassword(
plaintext: string,
hash: string
): Promise<boolean> {
return bcrypt.compare(plaintext, hash);
}
// Password policy enforcement
function validatePassword(pw: string): string[] {
const errors: string[] = [];
if (pw.length < 12) errors.push('Min 12 characters');
if (!/[A-Z]/.test(pw)) errors.push('Need uppercase');
if (!/[0-9]/.test(pw)) errors.push('Need digit');
if (!/[^A-Za-z0-9]/.test(pw)) errors.push('Need special char');
return errors;
}JWT Security Best Practices
// JWT with RS256 (asymmetric — recommended)
import jwt from 'jsonwebtoken';
import fs from 'fs';
const privateKey = fs.readFileSync('./keys/private.pem');
const publicKey = fs.readFileSync('./keys/public.pem');
function signToken(payload: object): string {
return jwt.sign(payload, privateKey, {
algorithm: 'RS256',
expiresIn: '15m', // Short-lived access tokens
issuer: 'myapp.com',
audience: 'myapp-api',
});
}
function verifyToken(token: string): jwt.JwtPayload {
return jwt.verify(token, publicKey, {
algorithms: ['RS256'], // CRITICAL: whitelist algorithms
issuer: 'myapp.com',
audience: 'myapp-api',
}) as jwt.JwtPayload;
}JWT security essentials: always whitelist signing algorithms (disable "none"), use short-lived access tokens with long-lived refresh tokens, validate issuer, audience, and expiration, and store refresh tokens in HttpOnly cookies.
OAuth 2.0 / OIDC Flows
| Flow | Use Case | Security Note |
|---|---|---|
| Authorization Code + PKCE | SPAs, mobile apps, server apps | Recommended for all clients |
| Client Credentials | Service-to-service (M2M) | Trusted backends only |
| Device Code | Browserless devices (CLI, IoT) | Short-lived polling codes |
| Implicit (Legacy) | Deprecated | Insecure — migrate to PKCE |
RBAC vs ABAC Authorization Models
// RBAC middleware example (Express.js)
type Role = 'admin' | 'editor' | 'viewer';
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete', 'manage_users'],
editor: ['read', 'write'],
viewer: ['read'],
};
function requirePermission(permission: string) {
return (req: Request, res: Response, next: NextFunction) => {
const userRole = req.user?.role as Role;
if (!permissions[userRole]?.includes(permission)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
// Usage
app.delete('/api/posts/:id',
authenticate,
requirePermission('delete'),
deletePostHandler
);Input Validation & Data Sanitization
All user input is untrusted. Validation must be enforced server-side (client-side validation is UX only), and should follow a whitelist-over-blacklist approach.
// Zod schema validation (TypeScript)
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(254),
name: z.string().min(1).max(100)
.regex(/^[a-zA-Z\s\-']+$/), // Whitelist chars
age: z.number().int().min(13).max(150),
role: z.enum(['user', 'editor']), // Enum whitelist
bio: z.string().max(500).optional(),
});
// In route handler
app.post('/api/users', (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error.flatten(),
});
}
// result.data is typed and validated
createUser(result.data);
});Input Validation Checklist
- Validate type, length, range, and format
- Use allowlists (whitelists) over blocklists
- Validate Content-Type headers
- Apply context-aware encoding for HTML output
- Normalize Unicode before validation
- Restrict file upload types, sizes, and storage locations
- Server-side validation cannot be bypassed — client-side is supplementary
XSS Prevention (Cross-Site Scripting)
XSS attacks inject malicious scripts into pages to steal cookies, hijack sessions, or redirect users. Prevention requires defense in depth with output encoding and CSP.
// Context-aware output encoding
import DOMPurify from 'dompurify';
// HTML context — sanitize user-generated HTML
const safeHTML = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
ALLOWED_ATTR: ['href'],
});
// JavaScript context — NEVER interpolate into JS
// BAD: <script>var x = "\${userInput}";</script>
// GOOD: Pass data via data attributes or JSON
// URL context — validate scheme
function isSafeUrl(url: string): boolean {
try {
const parsed = new URL(url);
return ['http:', 'https:'].includes(parsed.protocol);
} catch {
return false;
}
}CSRF Prevention (Cross-Site Request Forgery)
CSRF attacks trick authenticated users into performing unintended actions. Modern prevention combines SameSite cookies with CSRF tokens for double protection.
// Express.js CSRF protection setup
import csrf from 'csurf';
import cookieParser from 'cookie-parser';
app.use(cookieParser());
// Cookie settings for session
app.use(session({
cookie: {
httpOnly: true, // No JS access
secure: true, // HTTPS only
sameSite: 'lax', // CSRF protection layer 1
maxAge: 3600000, // 1 hour
},
// ... session store config
}));
// CSRF token middleware (layer 2)
const csrfProtection = csrf({ cookie: true });
app.use(csrfProtection);
// Provide token to client
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// Token auto-validated on POST/PUT/DELETE
app.post('/transfer', (req, res) => {
// csrfProtection already verified the token
processTransfer(req.body);
});SQL Injection Prevention
SQL injection allows attackers to execute arbitrary database queries. The only reliable defense is parameterized queries — never string concatenation.
// VULNERABLE — never do this
const query = `SELECT * FROM users WHERE id = \${userId}`;
// SAFE — parameterized query (node-postgres)
const { rows } = await pool.query(
'SELECT * FROM users WHERE id = $1 AND org_id = $2',
[userId, orgId]
);
// SAFE — ORM (Prisma)
const user = await prisma.user.findUnique({
where: { id: userId },
select: { id: true, name: true, email: true },
});
// SAFE — Query builder (Knex)
const users = await knex('users')
.where({ org_id: orgId })
.andWhere('created_at', '>', since)
.select('id', 'name', 'email');Database Security Hardening
- Least privilege — application accounts get only needed permissions
- Use read replicas for reporting queries
- Enable query logging and slow-query alerting
- Network segmentation — databases never exposed to public internet
- Encrypted connections (require SSL/TLS)
Secrets & Credential Management
Leaked secrets are among the most common security incidents. GitHub detects thousands of leaked secrets daily. Proper secrets management is foundational to secure infrastructure.
# .gitignore — MUST include these
.env
.env.*
*.pem
*.key
*.p12
credentials.json
service-account.json
# Pre-commit hook for secret detection
# Install: pip install detect-secrets
# .pre-commit-config.yaml
repos:
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']Secrets Management Solutions
| Solution | Type | Best For |
|---|---|---|
| HashiCorp Vault | Self-hosted/Cloud | Large orgs, dynamic secrets |
| AWS Secrets Manager | Cloud-native | AWS ecosystem |
| GCP Secret Manager | Cloud-native | GCP ecosystem |
| Azure Key Vault | Cloud-native | Azure ecosystem |
| SOPS + Age | File encryption | GitOps, small teams |
| Doppler | SaaS | Multi-env secret syncing |
// Fetching secrets at runtime (Node.js + AWS)
import { SecretsManagerClient,
GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManagerClient({ region: 'us-east-1' });
async function getSecret(name: string): Promise<string> {
const cmd = new GetSecretValueCommand({
SecretId: name,
});
const resp = await client.send(cmd);
return resp.SecretString ?? '';
}
// Usage — never hardcode secrets
const dbPassword = await getSecret('prod/db/password');Dependency Scanning & SCA
Software supply chain attacks are increasing. Every dependency is a potential attack surface. Automated dependency scanning should run on every CI build.
# npm audit — built-in Node.js
npm audit --production
npm audit fix
# Snyk — comprehensive SCA
npx snyk test
npx snyk monitor # continuous monitoring
# pip-audit — Python
pip-audit --require-hashes
# cargo audit — Rust
cargo install cargo-audit
cargo audit
# Trivy — container + filesystem
trivy fs --severity HIGH,CRITICAL .
trivy image myapp:latestSupply Chain Security Strategies
- Pin dependency versions (package-lock.json, yarn.lock)
- Enable Dependabot or Renovate for automated updates
- Review new dependencies for maintenance status and security history
- Use Socket.dev to detect suspicious package behavior
- Consider private registries (Verdaccio, Artifactory)
- Verify package signatures (npm provenance, sigstore)
Content Security Policy (CSP)
CSP is one of the most effective HTTP headers for preventing XSS. It specifies which resource origins the browser is allowed to load, fundamentally limiting the impact of script injection.
# Strict CSP — recommended starting point
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{RANDOM}';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.myapp.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
# Nonce-based CSP in Express.js
import crypto from 'crypto';
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.cspNonce = nonce;
res.setHeader('Content-Security-Policy',
`default-src 'self'; ` +
`script-src 'self' 'nonce-\${nonce}'; ` +
`style-src 'self' 'unsafe-inline'; ` +
`img-src 'self' data: https:;`
);
next();
});CORS (Cross-Origin Resource Sharing)
CORS controls which external domains can access your API. Misconfigured CORS policies are a common security vulnerability.
// Secure CORS configuration (Express.js)
import cors from 'cors';
// NEVER use: cors({ origin: '*' }) for authenticated APIs
const allowedOrigins = [
'https://myapp.com',
'https://admin.myapp.com',
];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // Allow cookies
maxAge: 86400, // Preflight cache: 24h
}));API Security
APIs are the primary attack surface of modern applications. Secure APIs require authentication, authorization, rate limiting, input validation, and proper error handling.
// Rate limiting with sliding window (Express)
import rateLimit from 'express-rate-limit';
// Global rate limit
const globalLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
standardHeaders: true,
legacyHeaders: false,
});
// Strict limit for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 5 attempts per 15 min
skipSuccessfulRequests: true,
});
app.use('/api/', globalLimiter);
app.use('/api/auth/login', authLimiter);
app.use('/api/auth/register', authLimiter);API Security Checklist
| Layer | Measure | Tools/Methods |
|---|---|---|
| Authentication | OAuth 2.0 / JWT | Auth0, Clerk, Keycloak |
| Authorization | RBAC / ABAC | Casbin, OPA, Cedar |
| Rate Limiting | Sliding window / Token bucket | express-rate-limit, Redis |
| Input Validation | Schema validation | Zod, Joi, AJV |
| Request Limits | Payload size, query depth | body-parser, graphql-depth-limit |
| Audit Logging | Structured logging | Winston, Pino, ELK |
Encryption: At Rest & In Transit
Encryption protects data from unauthorized access. Transit encryption (TLS) protects network communication, while at-rest encryption protects stored data. Both are foundational for compliance (GDPR, HIPAA, PCI DSS).
Encryption In Transit (TLS)
# Nginx TLS 1.3 configuration
server {
listen 443 ssl http2;
server_name myapp.com;
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:
ECDHE-RSA-AES128-GCM-SHA256:
ECDHE-ECDSA-AES256-GCM-SHA384:
ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS — force HTTPS for 2 years
add_header Strict-Transport-Security
"max-age=63072000; includeSubDomains; preload" always;
}Encryption At Rest
// AES-256-GCM encryption (Node.js)
import crypto from 'crypto';
const ALGORITHM = 'aes-256-gcm';
interface EncryptedData {
iv: string;
tag: string;
ciphertext: string;
}
function encrypt(plaintext: string, key: Buffer): EncryptedData {
const iv = crypto.randomBytes(12); // 96-bit IV for GCM
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
let ciphertext = cipher.update(plaintext, 'utf8', 'hex');
ciphertext += cipher.final('hex');
return {
iv: iv.toString('hex'),
tag: cipher.getAuthTag().toString('hex'),
ciphertext,
};
}
function decrypt(data: EncryptedData, key: Buffer): string {
const decipher = crypto.createDecipheriv(
ALGORITHM, key,
Buffer.from(data.iv, 'hex')
);
decipher.setAuthTag(Buffer.from(data.tag, 'hex'));
let plaintext = decipher.update(data.ciphertext, 'hex', 'utf8');
plaintext += decipher.final('utf8');
return plaintext;
}| Scenario | Recommended Algorithm | Notes |
|---|---|---|
| Password hashing | Argon2id / bcrypt | Irreversible, salted |
| Symmetric encryption | AES-256-GCM | Authenticated encryption, tamper-proof |
| Asymmetric encryption | RSA-OAEP / X25519 | Key exchange, digital signatures |
| Data integrity | HMAC-SHA256 | Verify no tampering |
| Token signing | EdDSA / RS256 | JWTs, API signatures |
Security Headers Configuration
Security headers are among the simplest yet most effective defenses. Properly configured security headers prevent a wide range of common attacks.
// Comprehensive security headers middleware
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
frameAncestors: ["'none'"],
},
},
hsts: {
maxAge: 63072000, // 2 years
includeSubDomains: true,
preload: true,
},
referrerPolicy: {
policy: 'strict-origin-when-cross-origin',
},
}));
// Additional headers not covered by Helmet
app.use((req, res, next) => {
res.setHeader('Permissions-Policy',
'camera=(), microphone=(), geolocation=()');
res.setHeader('X-Permitted-Cross-Domain-Policies', 'none');
next();
});Security Headers Reference
| Header | Recommended Value | Protects Against |
|---|---|---|
| Strict-Transport-Security | max-age=63072000; includeSubDomains; preload | Downgrade attacks, MITM |
| Content-Security-Policy | See CSP section above | XSS, data injection |
| X-Content-Type-Options | nosniff | MIME-type sniffing |
| X-Frame-Options | DENY | Clickjacking |
| Referrer-Policy | strict-origin-when-cross-origin | Information leakage |
| Permissions-Policy | camera=(), microphone=(), geolocation=() | Feature abuse |
| Cache-Control | no-store (sensitive pages) | Cache poisoning, data leaks |
DevSecOps CI/CD Integration
DevSecOps embeds security checks into every stage of the development workflow, achieving "shift-left security" — finding and fixing vulnerabilities as early as possible when the cost is lowest.
# GitHub Actions — security pipeline
name: Security Pipeline
on: [push, pull_request]
jobs:
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verified
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm audit --audit-level=high
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: \${{ secrets.SNYK_TOKEN }}
container-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp:test .
- uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:test
severity: HIGH,CRITICAL
exit-code: 1DevSecOps Toolchain
| Stage | Tools | Checks |
|---|---|---|
| Pre-commit | detect-secrets, git-secrets, truffleHog | Secret leak detection |
| SAST | Semgrep, CodeQL, SonarQube | Code vulnerability patterns |
| SCA | Snyk, npm audit, Dependabot | Known CVE vulnerabilities |
| Container Scan | Trivy, Grype, Snyk Container | Image vulnerabilities and config |
| IaC Scan | Checkov, tfsec, KICS | Infra misconfiguration |
| DAST | OWASP ZAP, Burp Suite, Nuclei | Runtime vulnerabilities |
Security Architecture Patterns
Zero Trust Architecture
The core principle of Zero Trust is "never trust, always verify." Every request must be authenticated, authorized, and encrypted, regardless of whether it originates from inside or outside the network.
- Micro-segmentation — services communicate via mTLS
- Verify identity and permissions on every request
- Least privilege access — grant only what is needed
- Continuous monitoring and logging — assume breach
Threat Modeling (STRIDE)
| Threat | Meaning | Mitigation |
|---|---|---|
| Spoofing | Impersonating identity | Strong auth, MFA |
| Tampering | Modifying data | Integrity checks, signatures |
| Repudiation | Denying actions | Audit logs, non-repudiation |
| Info Disclosure | Exposing information | Encryption, access control |
| DoS | Denial of service | Rate limiting, CDN, auto-scaling |
| Elevation | Privilege escalation | RBAC, least privilege |
Security Logging & Monitoring
Without logging and monitoring, security incidents cannot be detected or responded to. Structured logging and real-time alerting are the foundation of security operations.
// Structured security logging (Pino)
import pino from 'pino';
const logger = pino({
level: 'info',
redact: ['req.headers.authorization',
'req.headers.cookie',
'body.password'], // Never log secrets
});
// Security event logging
function logSecurityEvent(event: {
type: string;
userId?: string;
ip: string;
details: string;
severity: 'low' | 'medium' | 'high' | 'critical';
}) {
logger.warn({
securityEvent: true,
...event,
timestamp: new Date().toISOString(),
});
}
// Example: log failed authentication
logSecurityEvent({
type: 'AUTH_FAILURE',
ip: req.ip,
details: 'Invalid credentials for user@example.com',
severity: 'medium',
});Conclusion: Building a Security Culture
Application security is not a one-time task but a continuous engineering practice. From the foundational defenses of the OWASP Top 10 to automated DevSecOps integration, every security layer adds depth to your overall defense. Treat security as a feature, not a burden — the time invested in security pays enormous dividends in preventing data breaches and maintaining user trust.
Action checklist: 1) Enable security headers now; 2) Audit all SQL queries for parameterization; 3) Configure CSP; 4) Add dependency scanning to CI; 5) Implement a secrets management solution; 6) Conduct regular threat modeling and penetration testing. Security is a journey, not a destination.
Frequently Asked Questions
What is the OWASP Top 10 and why does it matter?
The OWASP Top 10 is a regularly updated list of the most critical web application security risks published by the Open Web Application Security Project. It matters because it represents a broad consensus on the most dangerous flaws. Many compliance frameworks (PCI DSS, SOC 2, ISO 27001) reference OWASP as a baseline standard. Addressing the Top 10 significantly reduces your attack surface.
What is the difference between authentication and authorization?
Authentication (AuthN) verifies identity — confirming that a user is who they claim to be through credentials like passwords, tokens, or biometrics. Authorization (AuthZ) determines permissions — what an authenticated user is allowed to do. Authentication answers "who are you?" while authorization answers "what can you access?" Both are required for secure systems, and they should be implemented as separate, composable layers.
How do I prevent SQL injection in modern applications?
Use parameterized queries or prepared statements exclusively — never concatenate user input into SQL strings. ORMs like Prisma, SQLAlchemy, and ActiveRecord use parameterized queries by default. Additionally, apply the principle of least privilege to database accounts, validate and sanitize all inputs, and use a Web Application Firewall (WAF) as an additional defense layer. Regular code reviews and SAST tools can catch injection vulnerabilities early.
What is Content Security Policy (CSP) and how do I implement it?
Content Security Policy is an HTTP response header that controls which resources a browser is allowed to load for a page. A strict CSP prevents XSS by blocking inline scripts and restricting script sources. Start with Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; and tighten from there. Use nonce-based or hash-based CSP for inline scripts instead of 'unsafe-inline'. Report violations with report-uri or report-to directives.
How should I manage secrets like API keys and database passwords?
Never store secrets in source code, environment files committed to version control, or client-side code. Use a dedicated secrets manager such as HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault. For local development, use .env files that are gitignored. In CI/CD, use pipeline-level secret variables. Rotate secrets regularly, audit access, and use short-lived tokens where possible. Pre-commit hooks with tools like git-secrets or truffleHog can prevent accidental commits of credentials.
What security headers should every web application set?
Essential security headers include: Strict-Transport-Security (HSTS) to enforce HTTPS; Content-Security-Policy to prevent XSS; X-Content-Type-Options: nosniff to prevent MIME-type sniffing; X-Frame-Options: DENY or SAMEORIGIN to prevent clickjacking; Referrer-Policy: strict-origin-when-cross-origin to limit referrer leakage; Permissions-Policy to disable unnecessary browser features; and Cache-Control: no-store for sensitive pages. Use securityheaders.com to audit your configuration.
How do I secure REST APIs against common attacks?
Secure REST APIs by implementing proper authentication (OAuth 2.0, JWT with RS256), rate limiting per endpoint and per user, input validation on all parameters, CORS whitelisting, request size limits, and pagination for list endpoints. Use API keys for identification (not authentication), validate Content-Type headers, return minimal error information to clients, log all access attempts, and version your API to maintain backward compatibility during security updates.
How do I integrate security into a CI/CD pipeline (DevSecOps)?
DevSecOps integrates security checks into every stage of CI/CD. In the commit stage, run pre-commit hooks for secret detection (git-secrets, truffleHog). In the build stage, run SAST tools (Semgrep, CodeQL, SonarQube) and dependency scanning (npm audit, Snyk, Dependabot). In the test stage, run DAST tools (OWASP ZAP, Burp Suite) against staging environments. In the deploy stage, scan container images (Trivy, Grype) and validate Infrastructure as Code (Checkov, tfsec). Fail the pipeline on critical findings and track security debt alongside technical debt.