OWASP API Top 10 for HTTP, GraphQL, and gRPC endpoints: input validation, route rate limiting, mass assignment, response caching, and gateway-versus-service control placement. Use when generating or reviewing HTTP handlers, GraphQL resolvers, gRPC service methods, or any API endpoint change.
Installs into .claude/skills of the current project.
Are you the author of Api Security?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/shieldnet-360-api-security)
---
name: api-security
description: "OWASP API Top 10 for HTTP, GraphQL, and gRPC endpoints: input validation, route rate limiting, mass assignment, response caching, and gateway-versus-service control placement. Use when generating or reviewing HTTP handlers, GraphQL resolvers, gRPC service methods, or any API endpoint change."
---
<!-- Native skill bundle for agent-skills (cross-tool convention). Generated by `secure-vibe dev regenerate`. -->
<!-- Do not edit by hand; the source of truth is skills/api-security/SKILL.md. -->
# API Security
OWASP API Top 10 for HTTP, GraphQL, and gRPC endpoints: input validation, route rate limiting, mass assignment, response caching, and gateway-versus-service control placement. Use when generating or reviewing HTTP handlers, GraphQL resolvers, gRPC service methods, or any API endpoint change.
## ALWAYS
- Require authentication on every non-public endpoint. Default to authenticated; opt out for genuinely public routes by explicit annotation.
- Consult `auth-security` for **who the caller is and what they may touch**. It owns the credential (algorithm pinning, expiry, lifetime, session, CSRF) and the permission (object-level BOLA/IDOR checks, multi-key routes, streaming subjects, function-level role gating). Any endpoint that reads or writes a resource by id needs it; this skill covers everything else about the endpoint.
- Validate all request inputs against an explicit schema (JSON Schema, Pydantic, Zod, validator/v10 struct tags). Reject early; never propagate untrusted input deeper.
- Enforce rate limits at the route level for authentication endpoints, password reset, and any expensive operation.
- Consult `error-handling-security` for what an error response may carry. It owns the boundary between what crosses to the client and what stays in the log, including presence-of-record disclosure (`User not found` vs `Invalid credentials`).
- Include `Cache-Control: no-store` on responses containing personal or sensitive data.
## NEVER
- Use sequential integer IDs in URLs for resources accessible across tenants. Use UUIDs or unguessable opaque IDs.
- Mass-assign request bodies directly to ORM models (`User(**request.json)`) — this enables privilege escalation when the model has admin fields the user shouldn't control.
- Key a rate-limit / lockout counter on a **client-controllable header** (leftmost `X-Forwarded-For`, `X-Real-IP`, `Forwarded`) — rotating it yields a fresh bucket per request and defeats the limit. Derive the client IP from the trusted-proxy hop count (or key on the authenticated user), and fail closed on limiter error.
- Use `HTTP GET` for any state-changing operation — GET should be safe and idempotent.
- Rely on **network position** (IP allowlist, VPN, private subnet, "internal only", a WAF/edge rule) as the *only* control on a sensitive endpoint. Reachability is not authentication: the moment there's an SSRF, a compromised internal host, a tenant on the network, or a boundary change, an unauthenticated "internal" endpoint (`permission_classes = [AllowAny]`, no `RequireAuth`) is wide open. Enforce auth/authz at the service itself, behind any network control.
- Place security controls (auth, field-stripping, CSRF, rate-limit, input validation) only at a gateway / BFF / proxy while the backend service is **also directly reachable**. An attacker calls the service directly and bypasses every proxy-layer control — controls must live at the service that owns the data. (A common variant: the gateway checks that a JWT is *present* but the service never checks the caller's *role* or *object-level ownership* — the service reads the subject id from the body/path/query and trusts it.)
## KNOWN FALSE POSITIVES
- Public marketing-site endpoints serving anonymous traffic legitimately have no auth and no rate limits beyond the load balancer.
- Sequential IDs in paths are fine for genuinely public, non-tenant-scoped resources (e.g. blog post slugs, public product catalog items).
- Health-check endpoints (`/healthz`, `/ready`) intentionally bypass auth.
- A network control (mTLS service mesh, NetworkPolicy, private ingress) is fine as **defense-in-depth** — the anti-pattern is only when it's the *sole* control and the service itself authenticates nothing.
- Mutual-TLS / SPIFFE workload identity between services **is** authentication (a cryptographic caller identity), not mere network position — mTLS-authenticated service-to-service calls are fine even on a private network.
## Reference files
Read these only when the task calls for them.
- `references/verifying-findings.md`