A05 Security Misconfiguration
A05:2021 — Security Misconfiguration — covers default credentials, verbose errors, unnecessary features, missing security headers, outdated software. The bug class where humans fail, not code.
Defaults, headers, secrets, scan
EXAMPLE
# A05 is the most common real-world finding in pen-tests. The fix is process, not code.
# === Common misconfigurations ===
# 1) Default credentials
# - admin / admin on the firewall
# - root / root on MongoDB without auth
# - postgres user with no password
# - Tomcat manager / Tomcat manager
# - Elasticsearch with no auth on port 9200
#
# Fix:
# - Rotate ALL defaults at install
# - Enforce password requirements
# - Use SSO / OIDC for admin tools when possible
# - Disable default accounts you don't use
# 2) Verbose error messages in production
# Bad: stack trace, SQL query, file path leaked to attacker
app.use((err, req, res, next) => {
res.status(500).send(err.stack); # NEVER in production
});
# Good
app.use((err, req, res, next) => {
log.error({ err, reqId: req.id }, 'unhandled');
res.status(err.status ?? 500).json({
error: process.env.NODE_ENV === 'production' ? 'internal' : err.message,
requestId: req.id,
});
});
# 3) Missing security headers
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'nonce-RANDOM'"],
styleSrc: ["'self'", "'nonce-RANDOM'"],
imgSrc: ["'self'", 'data:', 'https:'],
objectSrc: ["'none'"],
baseUri: ["'self'"],
},
},
hsts: { maxAge: 31_536_000, includeSubDomains: true, preload: true },
xFrameOptions: { action: 'deny' },
}));
# Or set manually:
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
# Test with: securityheaders.com
# 4) Unnecessary features enabled
# - PHP eval enabled
# - Directory listing on web server
# - Sample apps / docs accessible (e.g. /phpinfo.php, Tomcat docs)
# - Debug endpoints (/debug, /healthcheck with sensitive info)
# - GraphQL introspection on prod
# - HTTP TRACE method enabled (XST attacks)
nginx.conf — disable directory listing
# autoindex off;
# Apache
# Options -Indexes
# 5) Outdated software / dependencies
# Track + patch:
# - OS packages (apt / yum updates)
# - Base Docker images (rebuild monthly minimum)
# - npm / pip / cargo deps (Renovate, Dependabot)
# - Application servers + runtimes
# - Web framework versions
# Automate:
# - Renovate / Dependabot for code deps
# - Trivy / Snyk for image + dep scanning in CI
# - Patch SLA: critical < 7 days, high < 30, medium < 90
# 6) Open admin / debug ports
ss -tlnp # list listening services
# Internal: 6379 Redis, 5432 Postgres, 27017 Mongo, 9200 Elastic, 9090 Prom
# These should ONLY listen on localhost or VPC.
# nginx — only listen on localhost
listen 127.0.0.1:8080;
# Docker — bind to 127.0.0.1, not 0.0.0.0
docker run -d -p 127.0.0.1:5432:5432 postgres
# Kubernetes — don't expose admin services with type=LoadBalancer
# 7) Secrets in source / config files
# .env committed to git
# config.yml with passwords
# Dockerfile with ENV API_KEY=...
# JS source with apiKey hard-coded
# Fix:
# - gitleaks / trufflehog in pre-commit + CI
# - Use KMS / Secrets Manager / Vault
# - Rotate any leaked secret at source (force-push is NOT enough)
# - Use OIDC-assumed roles instead of long-lived API keys where possible
# 8) Permissive S3 / cloud bucket policies
# Common pattern: public bucket meant to be private
# - 'AmazonS3FullAccess' on a Lambda role that needs s3:GetObject
# - 'Allow': '*' on Resource
# - Public read on a backup bucket
# Fix:
# - Block Public Access at account level
# - IAM Access Analyzer to find externally-accessible resources
# - Periodic cloud security posture review (ScoutSuite, Prowler)
# 9) Insecure cookies
res.cookie('session', sid); # missing flags!
res.cookie('session', sid, { # correct
httpOnly: true,
secure: true,
sameSite: 'lax',
});
# 10) CORS misconfiguration
# Bad: 'Access-Control-Allow-Origin': '*' with 'Allow-Credentials: true' → browsers reject, but server is wrong anyway
# Bad: Reflecting Origin without allowlist:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', req.headers.origin); # trusts attacker!
res.header('Access-Control-Allow-Credentials', 'true');
next();
});
# Good:
import cors from 'cors';
app.use(cors({
origin: (origin, cb) => cb(null, ALLOWED.has(origin ?? '')),
credentials: true,
}));
# 11) Default error pages
# Express default 404 / 500 leak framework name + version.
# Fix: register custom handlers.
# 12) Sample / scaffold code shipped to prod
# - Express generator's debug routes
# - Spring's actuator endpoints exposed to internet
# - Django admin reachable without IP allowlist
# - WordPress admin without 2FA
# Fix: review what's reachable from the internet; restrict.
# === Detection ===
# 13) Automated scanning
# - Mozilla Observatory (security headers, TLS, redirects)
# - securityheaders.com (security headers only)
# - SSL Labs (TLS configuration)
# - OWASP ZAP (web app scanner)
# - Nuclei + community templates (CVE + misconfig)
# - testssl.sh (TLS scanner)
# - kube-bench / kube-hunter (Kubernetes)
# - Trivy / Snyk (image + dep scanning)
# 14) Cloud-native tools
# - AWS Config + Trusted Advisor
# - GCP Security Command Center
# - Azure Defender
# - CloudSploit, Prowler, ScoutSuite (multi-cloud)
# 15) Static analysis
# - Semgrep with security packs
# - Checkov (IaC misconfig)
# - tfsec (Terraform-specific)
# - kics (multi-IaC scanner)
# === Process ===
# 16) Configuration as code
# - Terraform / CloudFormation / Pulumi for cloud
# - Kustomize / Helm for Kubernetes
# - Docker for app config
# Reproducible, reviewable, auditable.
# 17) Hardening baselines
# - CIS Benchmarks (Linux, Kubernetes, AWS, Docker, Postgres, ...)
# - NIST SP 800-53 / 800-171 controls
# - Pre-built hardened AMIs / images
# 18) Patch management policy
# - Critical: 7 days
# - High: 30 days
# - Medium: 90 days
# - Low: best-effort
# Automate where possible:
# - Ubuntu unattended-upgrades for OS
# - Renovate / Dependabot for code
# - Reusable workflows in CI for rebuilding base images
# 19) Periodic security review
# - Quarterly external attack surface review
# - Annual third-party pen test
# - Continuous bug bounty for high-risk products
# - Quarterly access review (who has what?)
# === Real-world incidents ===
# Misconfig examples that became breaches:
# - Capital One 2019: misconfigured WAF + SSRF → S3 data exposed
# - MongoDB exposed without auth → mass deletion / ransomware
# - Elasticsearch with no auth → millions of records leaked
# - Default Tomcat credentials → RCE on production servers
# - Verbose error → SQL injection point + DB schema leaked
# - Hardcoded secrets in mobile app APK → backend credentials stolen
# === Best practices ===
# ✅ Disable / change defaults immediately on every service
# ✅ Lock down error verbosity in production
# ✅ Ship strong security headers via middleware
# ✅ Patch on a schedule with SLA
# ✅ Configuration as code (no manual prod tweaks)
# ✅ Periodic scans (CI + scheduled)
# ✅ Secrets in a manager, not in code
# ✅ Least privilege at every layer (cloud, app, DB)
# ✅ Minimise the public surface — internal services on private networks only
Why it matters
Misconfiguration is the most common real-world vuln — default creds, verbose errors, missing headers. The fix is process: IaC, automated scanning, patch SLAs, periodic reviews. Code review catches XSS; checklists catch this.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// A05 Security Misconfiguration — open S3 buckets, default creds, verbose errors. // Fix: IaC scanners (tfsec, checkov, kube-score), CIS benchmarks, immutable infra.Try it Yourself »
Discussion
Loading…