agentsclimarketplace

Api security

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/api-security

A curated pack of custom Claude Code skills for developers — installable as a Claude Code plugin marketplace.

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill api-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 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.

What its author says it does

Copied from the file, not written here

When to activate: API security, rate limiting, JWT auth, OAuth2, API keys, WAF, GraphQL security, BOLA, broken object level authorization

SKILL.md

6.3 KB, as published. Nobody here has run it

API Security Patterns

Authentication

# JWT verification — pin algorithm, check expiry
import jwt
from fastapi import HTTPException, Security
from fastapi.security import HTTPBearer

security = HTTPBearer()

def verify_token(token: str) -> dict:
    try:
        payload = jwt.decode(
            token,
            settings.JWT_PUBLIC_KEY,
            algorithms=["RS256"],   # Pin algorithm — never accept "none"
            options={"require": ["exp", "iat", "sub"]},
            audience="api.example.com",
        )
        return payload
    except jwt.ExpiredSignatureError:
        raise HTTPException(401, "Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(401, "Invalid token")

# API Key — hash stored, compared in constant time
import hashlib, hmac, secrets

def create_api_key() -> tuple[str, str]:
    raw = secrets.token_urlsafe(32)
    hashed = hashlib.sha256(raw.encode()).hexdigest()
    return raw, hashed          # return raw once, store hashed

def verify_api_key(provided: str, stored_hash: str) -> bool:
    provided_hash = hashlib.sha256(provided.encode()).hexdigest()
    return hmac.compare_digest(provided_hash, stored_hash)

Authorization — BOLA Prevention

# Broken Object Level Authorization — always check ownership
from fastapi import Depends

async def get_invoice(
    invoice_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    invoice = await db.get(Invoice, invoice_id)
    if not invoice:
        raise HTTPException(404)

    # CRITICAL: verify ownership — never skip this
    if invoice.owner_id != current_user.id:
        raise HTTPException(403, "Access denied")   # Not 404 — don't leak existence

    return invoice

# For admin endpoints — verify role server-side (not just in UI)
def require_role(role: str):
    def dependency(user: User = Depends(get_current_user)):
        if role not in user.roles:
            raise HTTPException(403, f"Role {role} required")
        return user
    return dependency

Rate Limiting

# FastAPI with slowapi
from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@app.post("/api/login")
@limiter.limit("5/minute")          # 5 attempts per minute per IP
async def login(request: Request, credentials: LoginRequest):
    ...

@app.post("/api/send-otp")
@limiter.limit("3/hour")            # Prevent OTP flooding
async def send_otp(request: Request):
    ...
# Nginx rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;

location /api/ {
    limit_req zone=api burst=20 nodelay;
    limit_req_status 429;
}

location /api/auth/ {
    limit_req zone=auth burst=3 nodelay;
    limit_req_status 429;
}

Input Validation

from pydantic import BaseModel, validator, constr
import re

class CreateUserRequest(BaseModel):
    username: constr(min_length=3, max_length=30, pattern=r'^[a-zA-Z0-9_]+$')
    email: str
    age: int

    @validator('email')
    def validate_email(cls, v):
        if not re.match(r'^[^@]+@[^@]+\.[^@]+$', v):
            raise ValueError('Invalid email format')
        return v.lower().strip()

    @validator('age')
    def validate_age(cls, v):
        if not 0 < v < 150:
            raise ValueError('Invalid age')
        return v

GraphQL Security

# Depth limiting — prevent deeply nested queries
from graphql import build_schema
from graphql_depth_limit import depth_limit_validator

schema = build_schema(type_defs)

# Reject queries deeper than 5 levels
validation_rules = [depth_limit_validator(max_depth=5)]

# Query complexity limiting
from graphql_query_complexity import QualityLimitRule

class ComplexityRule(QualityLimitRule):
    def __init__(self, context):
        super().__init__(context, max_complexity=100)

# Disable introspection in production
GRAPHQL_INTROSPECTION = settings.DEBUG  # False in prod

# Field-level authorization
@strawberry.type
class User:
    name: str

    @strawberry.field
    def email(self, info: Info) -> str:
        if info.context.user.id != self.id and not info.context.user.is_admin:
            raise PermissionError("Cannot view other users' email")
        return self._email

Security Headers

# FastAPI middleware
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware

class SecurityHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
        response.headers["Content-Security-Policy"] = "default-src 'self'"
        response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
        response.headers.pop("Server", None)    # Don't expose server info
        return response

app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.example.com"],  # Explicit, never "*"
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

API Security Checklist

Authentication: JWT pins alg, API keys hashed, OAuth PKCE for SPAs
Authorization: BOLA checked server-side on every resource access
Rate Limiting: login ≤5/min, OTP ≤3/hr, general API ≤100/min
Input: schema validation on all inputs, content-type enforced
Output: no stack traces, no internal paths, minimal error details
Transport: HTTPS only, HSTS header, TLS 1.2+ minimum
Headers: security headers set, Server header removed
GraphQL: depth limit, complexity limit, introspection off in prod
Logging: log auth failures, 4xx/5xx, but NOT request bodies with PII

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.