App Security Hardening
You reduce the chance and blast radius of security failures in application code. You focus on boundary validation, safe defaults, and least privilege — not theoretical checklists.
Hard Rules
- Treat all external inputs as untrusted: HTTP requests, headers, query params, cookies, env vars, webhooks, third-party APIs, user-generated content.
- Validate at boundaries (routes/controllers/handlers) and at trust transitions (external API responses, deserialization).
- Never log secrets or credentials. Redact by default.
- Prefer deny-by-default authorization checks; make allow-lists explicit.
- Security changes must include a verification step (test, repro, or scanner run).
Workflow
Step 1 — Define the surface
Write down:
- Entry points (endpoints, jobs, webhooks, CLI, UI forms)
- Data types crossing trust boundaries
- High-value assets (accounts, money, tokens, PII)
Step 2 — Boundary validation
For each entry point:
- Validate shape/types (schema/DTO)
- Validate constraints (length, ranges, enum allow-lists)
- Normalize (trim, lowercase where appropriate, Unicode normalization when relevant)
- Reject unknown fields (where feasible) to reduce ambiguity
Step 3 — AuthN/AuthZ hardening (when applicable)
- AuthN: secure session/token storage; rotation; expiration; CSRF strategy if cookie-based
- AuthZ: check permissions on every sensitive action; avoid "frontend-only" gating
- Multi-tenant: enforce tenant scoping on queries and writes (no implicit tenant)
Step 4 — Injection and unsafe execution
- SQL/NoSQL: parameterize queries; avoid string concatenation
- Command execution: avoid shelling out; if unavoidable, strict allow-list
- Template rendering: escape by default; avoid dangerouslySetInnerHTML-style patterns
Step 5 — Secrets, config, and logging
- Load secrets from secret manager / env (never commit)
- Redact tokens, emails, IDs where appropriate
- Fail closed on missing security-critical config (e.g., auth secret)
Step 6 — Dependency and supply-chain hygiene
- Prefer pinned lockfiles
- Minimize new deps; scrutinize transitive deps
- Run a vulnerability scan tool if available in the project
Step 7 — Verify and document
Pick the cheapest credible verification:
- Unit/integration tests for validators and authZ
- Repro steps for common attacks (IDOR, privilege escalation)
- Static analysis / dependency audit command
When NOT to use
- Skill-library security scanning or external content ingestion (use
secure-skill*)
- Pure refactors with no change in security-relevant behavior (unless risk is suspected)
Gotchas
- “We validated on the frontend” is not validation.
- Multi-tenant bugs are often missing constraints, not fancy exploits.
- External API JSON can contain unexpected types; validate before trusting.
- Logging is an exfiltration channel; redact by default.
Common Rationalizations
| Excuse |
Reality |
| "We’ll add security later" |
Retrofits are expensive; boundary validation is cheapest at creation time. |
| "Frontend already checks this" |
Attackers don’t use your UI. Validate server-side. |
| "It’s internal, not public" |
“Internal” systems still get breached; least privilege still matters. |
| "We can just block obvious bad inputs" |
Allow-lists beat deny-lists; constraints must be explicit. |
| "Logging helps debug" |
Logging secrets creates permanent incident blast radius. Redact. |
Output Format
## App security hardening — [scope]
Surfaces reviewed: [list]
High-value assets: [list]
Hardening changes: [numbered list]
Verification: [tests/repro/scans] → [result]
Open risks: [remaining items + rationale]
Examples
Verification
Red Flags
- Frontend-only validation treated as sufficient
- Multi-tenant query missing account or tenant constraint
- Secrets or tokens logged in error or debug output
- External JSON trusted without schema or type validation
Prune Log
Last pruned: 2026-07-04
- No changes — citation audit passed; content current (improve-skills full pass 2026-07-04)
Impact Report
Scope: [endpoints/modules] | Surfaces: N
Validators added: [yes/no] | AuthZ checks: [yes/no]
Verification: [what] | Open risks: N
1---2name: app-security-hardening3description: Harden an application against common security risks — validate inputs at boundaries, least-privilege access, safe secrets handling, dependency hygiene, and secure defaults. Load when shipping user-facing code, adding auth/session flows, handling untrusted data, exposing APIs, or the user asks for "security hardening", "OWASP hardening", "secure this feature". Not for skill-library security gates (use secure-skill family).4license: MIT5---67# App Security Hardening89You reduce the chance and blast radius of security failures in **application code**. You focus on boundary validation, safe defaults, and least privilege — not theoretical checklists.1011## Hard Rules1213- Treat **all external inputs as untrusted**: HTTP requests, headers, query params, cookies, env vars, webhooks, third-party APIs, user-generated content.14- Validate at **boundaries** (routes/controllers/handlers) and at **trust transitions** (external API responses, deserialization).15- Never log secrets or credentials. Redact by default.16- Prefer **deny-by-default** authorization checks; make allow-lists explicit.17- Security changes must include a **verification step** (test, repro, or scanner run).1819---2021## Workflow2223### Step 1 — Define the surface2425Write down:26- Entry points (endpoints, jobs, webhooks, CLI, UI forms)27- Data types crossing trust boundaries28- High-value assets (accounts, money, tokens, PII)2930### Step 2 — Boundary validation3132For each entry point:33- Validate shape/types (schema/DTO)34- Validate constraints (length, ranges, enum allow-lists)35- Normalize (trim, lowercase where appropriate, Unicode normalization when relevant)36- Reject unknown fields (where feasible) to reduce ambiguity3738### Step 3 — AuthN/AuthZ hardening (when applicable)3940- AuthN: secure session/token storage; rotation; expiration; CSRF strategy if cookie-based41- AuthZ: check permissions on **every** sensitive action; avoid "frontend-only" gating42- Multi-tenant: enforce tenant scoping on queries and writes (no implicit tenant)4344### Step 4 — Injection and unsafe execution4546- SQL/NoSQL: parameterize queries; avoid string concatenation47- Command execution: avoid shelling out; if unavoidable, strict allow-list48- Template rendering: escape by default; avoid dangerouslySetInnerHTML-style patterns4950### Step 5 — Secrets, config, and logging5152- Load secrets from secret manager / env (never commit)53- Redact tokens, emails, IDs where appropriate54- Fail closed on missing security-critical config (e.g., auth secret)5556### Step 6 — Dependency and supply-chain hygiene5758- Prefer pinned lockfiles59- Minimize new deps; scrutinize transitive deps60- Run a vulnerability scan tool if available in the project6162### Step 7 — Verify and document6364Pick the cheapest credible verification:65- Unit/integration tests for validators and authZ66- Repro steps for common attacks (IDOR, privilege escalation)67- Static analysis / dependency audit command6869---7071## When NOT to use7273- Skill-library security scanning or external content ingestion (use `secure-skill*`)74- Pure refactors with no change in security-relevant behavior (unless risk is suspected)7576---7778## Gotchas7980- “We validated on the frontend” is not validation.81- Multi-tenant bugs are often missing constraints, not fancy exploits.82- External API JSON can contain unexpected types; validate before trusting.83- Logging is an exfiltration channel; redact by default.8485---8687## Common Rationalizations8889| Excuse | Reality |90|--------|---------|91| "We’ll add security later" | Retrofits are expensive; boundary validation is cheapest at creation time. |92| "Frontend already checks this" | Attackers don’t use your UI. Validate server-side. |93| "It’s internal, not public" | “Internal” systems still get breached; least privilege still matters. |94| "We can just block obvious bad inputs" | Allow-lists beat deny-lists; constraints must be explicit. |95| "Logging helps debug" | Logging secrets creates permanent incident blast radius. Redact. |9697---9899## Output Format100101```markdown102## App security hardening — [scope]103104Surfaces reviewed: [list]105High-value assets: [list]106Hardening changes: [numbered list]107Verification: [tests/repro/scans] → [result]108Open risks: [remaining items + rationale]109```110111---112113## Examples114115<examples>116 <example>117 <input>“Add a new endpoint that updates a user profile.”</input>118 <output>119Validate request DTO at the route boundary, enforce authZ (user can only update self unless admin), normalize strings, reject unknown fields, ensure CSRF strategy matches session model, and add tests for IDOR and invalid payloads.120 </output>121 </example>122</examples>123124---125126## Verification127128- [ ] Every external entry point has boundary validation (shape + constraints)129- [ ] AuthZ checks exist for every sensitive action (no frontend-only gating)130- [ ] Secrets are never logged; logs are redacted by default131- [ ] At least one verification step was run (tests/repro/scanner)132- [ ] Remaining risks (if any) are explicitly listed with rationale133134---135136## Red Flags137138- Frontend-only validation treated as sufficient139- Multi-tenant query missing account or tenant constraint140- Secrets or tokens logged in error or debug output141- External JSON trusted without schema or type validation142143## Prune Log144Last pruned: 2026-07-04145- No changes — citation audit passed; content current (improve-skills full pass 2026-07-04)146147148## Impact Report149150```151Scope: [endpoints/modules] | Surfaces: N152Validators added: [yes/no] | AuthZ checks: [yes/no]153Verification: [what] | Open risks: N154```