| name | security-hardening |
| description | Use when hardening application configuration, implementing security headers, adding rate limiting, managing secrets and environment variables, auditing dependencies for vulnerabilities, assessing supply chain risk, responding to leaked secrets, or implementing defense-in-depth architecture. Triggers: "harden", "security headers", "CSP", "HSTS", "rate limiting", "secrets management", "dependency audit", "npm audit", "supply chain", "leaked secret". |
Security Hardening
Defense-in-depth configuration, secrets management, dependency auditing, and incident response for leaked credentials.
Core principle: Every layer of defense buys time and limits blast radius. No single control is sufficient.
When to Use
- Adding security headers to a new service
- Implementing rate limiting on auth endpoints
- Auditing or configuring secrets management
- Running dependency vulnerability scans
- Responding to a leaked credential
- Reviewing supply chain risk of dependencies
When NOT to Use
- Code-level security review (use security-review skill)
- Threat modeling (use threat-modeling skill)
- Compliance framework mapping (use compliance-and-governance skill)
Defense-in-Depth Layers
Perimeter → Network → Application → Data. Each layer assumes the one above has failed. No single control is sufficient.
Security Headers
import helmet from 'helmet';
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
}));
Required Headers Checklist
| Header | Value | Purpose |
|---|
Content-Security-Policy | See above | Prevents XSS, data injection |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Forces HTTPS |
X-Frame-Options | DENY or SAMEORIGIN | Prevents clickjacking |
X-Content-Type-Options | nosniff | Prevents MIME sniffing |
Referrer-Policy | strict-origin-when-cross-origin | Limits referrer data |
Permissions-Policy | camera=(), microphone=(), geolocation=() | Restricts browser APIs |
Rate Limiting
import rateLimit from 'express-rate-limit';
app.use('/api/auth/', rateLimit({
windowMs: 15 * 60 * 1000,
max: 10,
message: { error: 'Too many attempts. Try again in 15 minutes.' },
standardHeaders: true,
legacyHeaders: false,
}));
app.use('/api/', rateLimit({
windowMs: 1 * 60 * 1000,
max: 100,
}));
Endpoints requiring strict rate limiting:
POST /auth/login
POST /auth/register
POST /auth/forgot-password
POST /auth/reset-password
- Any endpoint that sends emails or SMS
Secrets Management
Environment Variable Rules
.env.example → Committed (template with placeholder values)
.env → NOT committed (real secrets)
.env.local → NOT committed (local overrides)
.gitignore must include:
.env
.env.*
!.env.example
*.pem
*.key
*.p12
Fail-Secure Pattern
SECRET_KEY = os.environ['SECRET_KEY']
DATABASE_URL = os.environ['DATABASE_URL']
SECRET_KEY = os.environ.get('SECRET_KEY') or 'default'
Pre-commit Check
git diff --cached | grep -iE "password|secret|api_key|token|AKIA|sk-|ghp_|Bearer"
Dependency Vulnerability Auditing
| Ecosystem | Command | Frequency |
|---|
| npm | npm audit --audit-level=high | Every CI run |
| Python | pip-audit | Every CI run |
| Ruby | bundle audit | Every CI run |
| Go | govulncheck ./... | Every CI run |
| Rust | cargo audit | Every CI run |
Audit Triage Decision Tree
Vulnerability found
├── Severity: Critical/High
│ ├── Reachable in production? → Fix immediately
│ ├── Dev-only dependency? → Fix soon, not a blocker
│ ├── Fix available? → Update to patched version
│ └── No fix available? → Replace dependency or document risk
├── Severity: Medium → Fix in next release cycle
└── Severity: Low → Track, fix during regular maintenance
Supply Chain Risk Assessment
Flag new dependencies that have: single maintainer, >1 year inactive, <1k downloads/week, multiple CVEs, or high-risk features (FFI, eval, shell exec). Anonymous maintainers warrant extra scrutiny.
Incident Response: Leaked Secrets
If a secret (API key, password, token) is found in code or commit history:
Immediate (within 1 hour)
- Revoke the credential immediately — assume it was accessed
- Rotate — generate a new credential
- Do NOT just push a new commit that deletes it — it's in git history
History Cleanup
pip install git-filter-repo
git filter-repo --path-glob '*.env' --invert-paths
git filter-repo --replace-text <(echo 'OLD_SECRET_VALUE==>REDACTED')
git push --force --all
git push --force --tags
Investigation (within 24 hours)
- Audit exposure window — When was the secret committed? Was repo public at any time?
- Check for abuse — Review the credential provider's audit logs for unauthorized use
- Notify — If there's any evidence of unauthorized access, follow breach notification procedures
Prevention
- Add
git-secrets or gitleaks as pre-commit hook
- Enable GitHub secret scanning on all repos
- Rotate all credentials on a schedule (90-day max for long-lived tokens)
CORS Configuration
app.use(cors({
origin: ['https://app.company.com', 'https://www.company.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
Container Security
FROM python:3.12-slim AS base # Minimal base, pinned version (not :latest)
FROM base AS builder # Multi-stage: isolate build deps
RUN pip install --user -r requirements.txt
FROM base AS runtime
RUN useradd -r -u 1001 appuser
USER appuser # Never run as root
COPY --from=builder /root/.local /home/appuser/.local
COPY --chown=appuser:appuser . .
Rules: no secrets in ENV or --build-arg (use --secret mount); pin image digests in CI; scan with trivy image <name> before deploy.
IaC Security (Terraform / Kubernetes)
Terraform: Block all S3 public access (block_public_acls = block_public_policy = true). Never use wildcard IAM (actions = ["*"]) — enumerate specific actions and resources.
Kubernetes — required securityContext for every pod:
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities: { drop: ["ALL"] }
Scan manifests with trivy config k8s/ or checkov -d . in CI.
Red Flags
Investigate immediately:
- Wildcard CORS (
*) in production
- No rate limiting on authentication endpoints
- Secrets in code (even if "old" or "test")
npm audit reporting Critical/High with no tracking issue
- Single-maintainer dependency with >10k downloads/week (high-value target)
verify: false or ssl: false in any production config
Verification Checklist
Headers:
Rate Limiting:
Secrets:
Dependencies:
CORS: