Api security
OWASP API Security Top 10 (2023) with production Next.js App Router code — access control, authentication, input validation, rate limiting, security headers, data exposure, supply chain, SSRFFrom its SKILL.md
npx -y skills add LeahyCC/claude-skills --skill api-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.8 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
API Security — OWASP API Security Top 10 (2023) for Next.js
Production-grade API security patterns for Next.js App Router. Covers every category in the OWASP API Security Top 10 (2023) with cross-references to the OWASP Web Top 10 (2021). Verified against the official OWASP specifications.
Architecture
Request → proxy.ts (auth gate) → Route Handler / Server Action
↓
Input Validation (Zod)
↓
Data Access Layer (auth + authz + DTO)
↓
Database (parameterized queries)
↓
Filtered Response (DTO, no raw records)
The Data Access Layer (DAL) is the central security architecture recommended by Next.js. All database access, authorization checks, and response filtering happen in one server-only module — never in components or actions directly.
Quick Reference
| Resource | OWASP API | OWASP Web | What It Covers |
|---|---|---|---|
| Access Control | API1, API5 | A01 | BOLA/IDOR, function-level auth, ownership checks |
| Authentication | API2 | A07, A02 | Session management, JWT, Clerk/Auth0, proxy.ts |
| Input Validation | API3, API8 | A03 | Zod schemas, SQL injection, XSS, Server Action validation |
| Rate Limiting | API4 | A04 | Per-endpoint limits, cost attacks, Upstash patterns |
| Security Headers | API8 | A05 | CSP, HSTS, CORS, Permissions-Policy, next.config |
| Data Exposure | API3, API6 | A04 | DAL pattern, DTOs, error sanitization, response filtering |
| Supply Chain | API9, API10 | A06, A08 | Dependencies, env vars, CI/CD, third-party APIs |
| SSRF & Logging | API7 | A09, A10 | URL validation, fetch safety, secure logging, audit trails |
Decision Matrix: "Where Do I Add Security?"
| Layer | What to Check | Why |
|---|---|---|
proxy.ts | Auth redirect, CSP nonce, rate limit headers | First line — blocks unauthenticated requests early |
Route Handler (route.ts) | Auth + authz, input validation, CORS, CSRF | Public HTTP endpoint — treat as untrusted |
Server Action ('use server') | Auth + authz, input validation, return filtering | Also a public HTTP endpoint — not protected by the UI |
Data Access Layer (data/*.ts) | Ownership checks, field filtering, parameterized queries | Last line — defense in depth |
next.config.ts | Security headers, redirects, allowed image domains | Static security baseline |
The #1 Mistake: Server Actions Are Public Endpoints
Every exported 'use server' function is reachable via direct POST request. Page-level auth does NOT protect actions on that page.
// FAIL — no auth check, anyone can call this via POST
'use server'
export async function deleteAccount(userId: string) {
await db.delete(users).where(eq(users.id, userId))
}
// PASS — validates auth AND ownership inside the action
'use server'
import { getCurrentUser } from '@/data/auth'
export async function deleteAccount() {
const user = await getCurrentUser()
if (!user) throw new Error('Unauthorized')
await db.delete(users).where(eq(users.id, user.id))
}
Notice: the PASS version doesn't accept userId as a parameter — it derives it from the session. This prevents IDOR (Insecure Direct Object Reference).
Decision Matrix: "Is This Secure?"
| Question | If No → | Resource |
|---|---|---|
| Does every Server Action re-verify auth? | Add getCurrentUser() check | Access Control |
| Does every Route Handler validate input with Zod? | Add schema validation | Input Validation |
| Are database queries parameterized? | Use tagged templates or ORM | Input Validation |
| Does the response return only needed fields? | Add DTO layer | Data Exposure |
| Are rate limits in place for auth endpoints? | Add Upstash/Arcjet | Rate Limiting |
| Are security headers configured? | Add to next.config.ts | Security Headers |
Is proxy.ts checking auth before page access? | Add auth middleware | Authentication |
| Are env vars only accessed in the DAL? | Move to server-only module | Supply Chain |
Does fetch() in server code validate URLs? | Add URL allowlist | SSRF & Logging |
| Are errors sanitized before returning to client? | Return generic messages | Data Exposure |
OWASP API Security Top 10 (2023) — Full Mapping
| # | Category | Severity | Resource |
|---|---|---|---|
| API1 | Broken Object Level Authorization (BOLA) | Critical | access-control |
| API2 | Broken Authentication | Critical | authentication |
| API3 | Broken Object Property Level Authorization | High | input-validation, data-exposure |
| API4 | Unrestricted Resource Consumption | High | rate-limiting |
| API5 | Broken Function Level Authorization | Critical | access-control |
| API6 | Unrestricted Access to Sensitive Business Flows | Medium | data-exposure |
| API7 | Server Side Request Forgery | High | ssrf-and-logging |
| API8 | Security Misconfiguration | Medium | security-headers, input-validation |
| API9 | Improper Inventory Management | Medium | supply-chain |
| API10 | Unsafe Consumption of APIs | Medium | supply-chain |
OWASP Web Top 10 (2021) — Cross-Reference
| # | Category | Mapped To |
|---|---|---|
| A01 | Broken Access Control | access-control |
| A02 | Cryptographic Failures | authentication |
| A03 | Injection | input-validation |
| A04 | Insecure Design | rate-limiting, data-exposure |
| A05 | Security Misconfiguration | security-headers |
| A06 | Vulnerable & Outdated Components | supply-chain |
| A07 | Auth Failures | authentication |
| A08 | Software & Data Integrity Failures | supply-chain |
| A09 | Logging & Monitoring Failures | ssrf-and-logging |
| A10 | SSRF | ssrf-and-logging |
Code Review Checklist
When reviewing API code in Next.js, verify:
- Every
'use server'function checks auth — no exceptions - No user-supplied IDs used for ownership — derive from session
- All inputs validated with Zod before use
- Database queries use parameterized queries or ORM
- Responses return DTOs, not raw database records
- Error messages don't leak stack traces, SQL, or internal paths
- Security headers set in
next.config.ts(CSP, HSTS, X-Frame-Options) - Rate limiting on auth endpoints and expensive operations
-
proxy.tsgates auth before page/action access - Environment variables accessed only in
server-onlymodules - No
fetch()to user-controlled URLs without allowlist - Dependencies audited — no known vulnerabilities
-
.env*.localin.gitignore
What ships with it: 9 files
83.5 KB alongside SKILL.md
resources/
- access-control.md9.1 KB
- authentication.md11.1 KB
- data-exposure.md10.2 KB
- input-validation.md10.0 KB
- rate-limiting.md9.4 KB
- security-headers.md9.5 KB
- ssrf-and-logging.md11.0 KB
- supply-chain.md8.8 KB
- README.md4.4 KB
Gives 2 of the 12 instructions most security skills give in ~1.9k tokens
Counted across 666 of the 889 authors here whose files we hold, read 2026-09-06
- Use parameterized queries for database accesshere, and in 82 of 666, across 79 files
- Hash passwords with BCryptin 55 of 666, across 39 files
- Implement rate limiting for public endpointshere, and in 48 of 666, across 34 files
- Use environment variables for secretsin 35 of 666
- Scan dependencies for vulnerabilitiesin 35 of 666, across 24 files
- Validate and sanitize all user inputin 35 of 666, across 32 files
- Add security headers to all responsesin 34 of 666, across 20 files
- Validate all external input at the system boundaryin 26 of 666, across 25 files
- Use parameterized queries to prevent SQL injectionin 25 of 666, across 13 files
- Store secrets in Vault or environment variablesin 25 of 666, across 10 files
- Run containers as a non-root userin 21 of 666, across 18 files
- Validate all input using Bean Validationin 19 of 666, across 5 files
Said here and by no other author read
- Derive user IDs from session instead of parameters
- Filter responses using data transfer objects
- Sanitize error messages before returning to client
- Gate page access using authentication middleware
- Access environment variables only in server-only modules
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.