Skip to content

OWASP API Security Top 10 Compliance

Snackbox aims to satisfy the OWASP API Security Top 10 (2023). The list targets the most critical security risks specific to REST APIs - covering authentication, authorization, resource consumption, and configuration concerns that generic application-security checklists tend to miss.

This document records the current compliance state, known gaps, and the steps needed to re-evaluate after any significant change to the API surface.

How to Evaluate

Run the two automated security checks before consulting the table below:

# Static security analysis of Go source (OWASP/CWE rule mapping)
make gosec

# Scan reachable Go code for known CVEs in dependencies
make govulncheck

For each item in the table, the Notes column describes the specific code locations and behaviors to verify. Re-evaluate any item whenever:

  • A new endpoint or role is added.
  • An authentication or authorization code path changes.
  • A dependency carrying a crypto, http, or io concern is updated.
  • Infrastructure changes (reverse proxy, TLS termination) affect header handling.

Evaluation

# Risk Status Notes
API1 Broken Object Level Authorization All write routes gate on JWT identity. /api/v1/users/{id} is admin-only - non-admins cannot read or modify other users' records. GET /me is scoped to the authenticated caller via ctxkey.UserID. Content write routes (/posts, /pages) enforce role checks at the router layer and ownership checks inside the handler for author-role deletes. Draft content visibility on public GET routes is gated in the handler, not the router.
API2 Broken Authentication JWT signed with HMAC-SHA256; algorithm is pinned in internal/middleware/auth.go - alg:none and RSA confusion attacks are rejected at parse time. Passwords are hashed with bcrypt. POST /auth/login and POST /auth/refresh are rate-limited per IP (default 10 req/s, configurable via AUTH_RATE_LIMIT). Refresh tokens are stored in SQLite and rotated on use. JWT_SECRET minimum length (32 chars) is enforced at startup via config.Validate(). Note: if JWT_SECRET is absent a random ephemeral secret is generated with a startup warning - all tokens are invalidated on restart; set an explicit secret in production.
API3 Broken Object Property Level Authorization Every resource uses a separate *Input struct; only the declared fields are decoded from the request body - mass assignment is structurally impossible. PUT /me (internal/handlers/users.go) pins Role to the existing value and clears Password before persisting, preventing privilege escalation and silent password changes through the profile endpoint. Password changes are routed exclusively through POST /auth/change-password.
API4 Unrestricted Resource Consumption ⚠️ MaxBody middleware caps all write-method request bodies at MAX_UPLOAD_SIZE (default 32 MiB). The media upload handler independently applies http.MaxBytesReader and checks the extension against a block-list. Pagination parameters (page, limit) are enforced on all list endpoints. However, rate limiting is applied only to auth endpoints (/auth/login, /auth/refresh) - authenticated write endpoints such as POST /media and POST /posts have no per-IP or per-user rate limit, leaving disk exhaustion and content-spam vectors open.
API5 Broken Function Level Authorization Auth middleware is applied selectively per route in internal/router/router.go, not globally, making the grant surface explicit and auditable. Three distinct role checks are used: Require (any authenticated user), RequireRole (exact single role), and RequireAnyRole (first-match list). Destructive admin operations (DELETE /users/{id}, PUT /settings, tag and navigation management) require the admin role. Content creation and editing require admin, author, or editor. Role comparison is exact-string - no implicit hierarchy means admin does not silently pass editor checks.
API6 Unrestricted Access to Sensitive Business Flows ⚠️ There is no self-registration endpoint - only an admin can create users, which significantly limits abuse of the account-creation flow. Rate limiting covers the authentication flows (login, refresh). However, authenticated content-creation endpoints (POST /posts, POST /pages, POST /media) carry no per-user or per-IP write rate limit. An authenticated user with author role could automate bulk content creation or media uploads within a session. For a self-hosted deployment with a small, trusted user set this risk is low, but it represents a gap relative to the standard.
API7 Server Side Request Forgery URL-typed fields accepted by the API (logo_url, icon_url in site settings; image_url in tags; title_image in posts and pages) are stored as opaque strings and returned in responses - they are never fetched server-side. There are no outbound HTTP client calls in the production code path. Input validation rejects non-http/https URL schemes before persistence, preventing javascript: or file: payloads from reaching clients via API responses.
API8 Security Misconfiguration CORS is correctly implemented: when a wildcard origin is configured, Access-Control-Allow-Credentials is withheld; when a specific origin matches, credentials are allowed and the exact origin is echoed. Security headers middleware sets X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, and Content-Security-Policy: default-src 'none' on every response - the strictest possible CSP, blocking all sub-resource loading and eliminating any stored-XSS execution path. HSTS is disabled by default and must be explicitly enabled via HSTS_MAX_AGE; this is intentional - sending HSTS over plain HTTP permanently locks users out, so opt-in is the correct default for a self-hosted deployment.
API9 Improper Inventory Management The full API surface is documented in api/openapi.yaml (OpenAPI 3.1), maintained manually and linted by vacuum in CI on every change - drift between implementation and spec is caught before merge. All endpoints are versioned under /api/v1/. GET /version exposes the build commit SHA and schema version for runtime inventory. No shadow or deprecated endpoints exist outside the spec.
API10 Unsafe Consumption of APIs Snackbox has no runtime dependencies on external APIs or third-party services. There are no outbound HTTP calls, no webhooks, and no SDK integrations in the production code path. The only external inputs are Go module dependencies, which are scanned for known vulnerabilities by govulncheck in CI. This item is effectively not applicable to the current architecture.

Summary

Fully compliant (8 / 10): Broken Object Level Authorization, Broken Authentication, Broken Object Property Level Authorization, Broken Function Level Authorization, Server Side Request Forgery, Security Misconfiguration, Improper Inventory Management, Unsafe Consumption of APIs.

Partially compliant (2 / 10):

Gap Severity Resolution
Unrestricted Resource Consumption (API4) Medium Add per-IP or per-user rate limiting to authenticated write endpoints, particularly POST /media, to prevent disk exhaustion
Sensitive Business Flows (API6) Low Add per-user write rate limits for content creation endpoints; acceptable risk for trusted self-hosted deployments with small user sets