Applies security fundamentals — input validation, authorization, secret handling, and safe defaults. Use when handling untrusted input, auth, sensitive data, webhooks, LLM prompts, file uploads, or reviewing code for vulnerabilities.
Installation
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Applies security fundamentals — input validation, authorization, secret handling, and safe defaults. Use when handling untrusted input, auth, sensitive data, webhooks, LLM prompts, file uploads, or reviewing code for vulnerabilities.
Hardening
Assume input is hostile and the network is not your friend. Most vulnerabilities come from trusting
something you shouldn't: user input, a token, a default config, an upstream response, or text stuffed
into an LLM prompt. Validate at boundaries, authorize every action, protect secrets, and
fail closed when something is wrong.
Security is not a final scan — it's built into every change that crosses a trust boundary. Retrofitting
after ship is slower and always misses something.
Run the security checklist alongside this process for a quick
pass. For supply-chain and dependency CVEs, pair with [[dependency-hygiene]]. For review workflow,
see [[review-gate]] and the security-auditor agent. For logging without leaking data, see
[[observability]].
When to Use
Any input crosses a trust boundary — HTTP params, JSON bodies, headers, cookies, uploads, webhooks
Authentication, authorization, sessions, JWTs, or API keys
Storing, transmitting, or logging sensitive or personal data (PII, payment, health)
Building or reviewing APIs, forms, admin tools, or multi-tenant features
LLM features with user or external content in prompts ([[llm-feature-engineering]])
Configuring production — TLS, headers, CORS, rate limits, secrets
Security-focused PR review or pre-launch pass ([[launch-readiness]])
Skip as the primary skill for pure internal refactors with no boundary change — still scan if the diff
touches auth, validation, or data access paths.
Process
Work in order. Map boundaries before diving into individual lines.
1. Map trust boundaries
List where untrusted data enters and where privileged actions happen:
Boundary
Untrusted inputs
Privileged actions
Public API
Query, body, headers
Read/write any user's data
Admin UI
Same + file uploads
Bulk export, role changes
Webhook handler
Raw body, signature header
Trigger internal jobs
Background job
Queue message, S3 key
DB writes, outbound calls
LLM pipeline
User text, retrieved docs
Tool calls, SQL generation
Browser
DOM, localStorage
Usually server must re-verify
Every arrow from untrusted → trusted is where controls must live. Client-side checks are UX, not
security — repeat validation and authorization server-side.
2. Validate input at the boundary — allowlist
Validate as soon as data enters your code — type, range, format, size — before it reaches SQL,
shell, templates, or business logic.
Good: email must match RFC-ish pattern; age 0–120; enum status in {pending, paid}
Bad: strip '<script>' and hope
Size limits on everything — body size, array length, string length, upload bytes. Unbounded input
is a DoS vector.
Types — parse JSON/schema strictly; reject unknown fields when the contract is fixed
([[interface-design]]).
Files — verify type and size; store outside web root; generate safe names — never use user path
as filesystem path (see uploads below).
Reject invalid input with a generic client message; log details server-side without secrets.
3. Never concatenate untrusted data into interpreters
Injection happens when untrusted data becomes syntax in SQL, shell, HTML, URLs, or templates.
Risk
Unsafe
Safe
SQL
"SELECT ... WHERE id = " + id
Parameterized queries / prepared statements
Shell
os.system("convert " + path)
Argument array APIs; no shell
HTML/XSS
Template with unescaped user HTML
Auto-escape templates; CSP
NoSQL
{$where: userInput}
Typed queries; operator allowlist
LDAP/XML
String concat
Library APIs with parameters
Output encoding matters at the sink — HTML context ≠ JS context ≠ URL context. Encode for where
data lands.
For redirects and links: allowlist destinations or map IDs to URLs — don't redirect(userSuppliedUrl)
(open redirect).
4. Authorize every protected action — server-side
Authentication (who are you?) ≠ authorization (may you do this to this object?).
For every endpoint and mutation:
Is the caller authenticated (if required)?
Is the caller allowed to perform this action?
On this specific resource (row, file, tenant)?
IDOR (insecure direct object reference) — classic bug:
GET /api/orders/123 — does 123 belong to this user/tenant?
DELETE /files/{id} — verify ownership before delete
Check authorization in the handler or service layer — not only hiding buttons in the UI.
Multi-tenant: scope every query by tenant_id (or equivalent) from the authenticated session —
never from a client-supplied field without verification ([[data-modeling]]).
Least privilege — roles, service accounts, and DB credentials get minimum permissions needed.
5. Sessions, tokens, and API keys
Use standard flows — OAuth2/OIDC, session cookies with HttpOnly, Secure, SameSite
Short-lived access tokens; refresh rotation where appropriate
Invalidate on logout, password change, and compromise
Don't store secrets in localStorage if avoidable — XSS steals them
API keys: scoped, rotatable, hashed at rest, never logged
Don't roll your own session tokens or password hashing — use vetted libraries (bcrypt/argon2,
framework session stores)
Verify webhook signatures (HMAC, timestamp) before trusting body — treat unsigned webhooks as
untrusted input.
6. Secrets and sensitive data
Never in:
Source code, git history, screenshots
Client bundles or mobile apps (assume extracted)
Logs, metrics labels, error messages, support tickets auto-filled from stack traces
URLs query params
Do:
Load from secrets manager / env at runtime ([[git-flow]] — scan diffs)
Rotate immediately if leaked — removal from git is not enough
Scrub PII and secrets from logs and traces ([[observability]])
Encrypt in transit (TLS everywhere) and at rest where required (DB, backups, disks)
Classify data — what's PII, payment, health — and model retention/delete paths ([[data-modeling]],
[[decision-docs]] for compliance rationale).
7. Fail closed with safe defaults
When auth, validation, or dependency checks fail — deny, don't degrade to open:
Wrong
Right
catch { return true }
Reject request; log internally
Missing role → full access
Missing role → deny
Config default auth=false
Default deny; explicit opt-in to expose
Error returns stack trace to client
Generic message; details in server log
Production defaults:
TLS on; HSTS where appropriate
Security headers — Content-Security-Policy, X-Content-Type-Options, X-Frame-Options or CSP
frame-ancestors
Restrictive CORS — explicit origins, not * with credentials
Debug endpoints and admin panels disabled or gated in prod
Rate limits and abuse protection on login, signup, expensive APIs, and LLM endpoints
Tenant from session only; every query scoped; test cross-tenant access explicitly.
LLM feature with tools
Allowlist tools; validate tool args; same authz as direct API; block instruction override in user text
where possible (delimiter patterns, separation — not foolproof, layer defenses).
Security regression in PR
Diff trust boundaries → focus review on changed edges → checklist → targeted tests for authz bypass.
Common Rationalizations
"Our users wouldn't do that." — Attackers aren't your users; bots scan everything.
"It's behind auth." — Authenticated users still attack IDs they shouldn't reach; check authz.
"We'll add validation later." — Exploitable the moment it ships.
"It's an internal service." — Internal boundaries get crossed; defense in depth.
"We validate on the client." — Client is attacker-controlled; server must enforce.
"CORS is fine — we use JWT." — Misconfigured CORS still matters; defense in layers.
"It's only a low-severity CVE." — Chain lows into highs; track and time-box fixes.