Strapi Security Testing: Default Credentials, API Endpoint Enumeration, File Upload Bypass, and GraphQL

Strapi is one of the most popular open-source headless CMS platforms, widely used as the backend API for JAMstack and Next.js applications. Its default-open permission model creates systematic security issues: every content type created in Strapi automatically generates REST API endpoints that are publicly accessible until an administrator explicitly restricts them in the Roles & Permissions settings — new collections are exposed by default; CVE-2023-22621 is a Server-Side Template Injection vulnerability in Strapi versions prior to 4.5.6 where the email reset template system renders user-supplied data as a Lodash template, enabling RCE through crafted password reset requests; the /api/auth/local/register endpoint allows public user registration by default, enabling mass account creation and potential abuse of authenticated endpoints; Strapi's file upload plugin accepts uploads at /api/upload without strict content type validation in some configurations; and GraphQL introspection on the Strapi GraphQL endpoint reveals the complete schema including all content types, field names, relationships, and enabled queries. This guide covers systematic Strapi security assessment.

Table of Contents

  1. Strapi Discovery and Version
  2. API Endpoint Enumeration and Public Content Access
  3. CVE-2023-22621 SSTI via Password Reset
  4. GraphQL Introspection and Schema Mapping
  5. Strapi Security Hardening

API Endpoint Enumeration and Public Content Access

# Strapi auto-generates REST endpoints for all content types
# By default ALL endpoints are publicly accessible (find-all, find-one)
# Developers must explicitly restrict permissions in Roles & Permissions

# Common Strapi content type naming — test standard patterns
COLLECTIONS=("articles" "posts" "products" "users" "orders" "customers"
             "pages" "blogs" "comments" "categories" "tags" "media"
             "settings" "configs" "events" "newsletters" "forms")

for COL in "${COLLECTIONS[@]}"; do
  CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    "https://example.com/api/${COL}" 2>/dev/null)
  if [ "$CODE" == "200" ]; then
    COUNT=$(curl -s "https://example.com/api/${COL}?pagination[pageSize]=1" \
      2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
meta = d.get('meta',{}).get('pagination',{})
print(meta.get('total',0))
" 2>/dev/null)
    echo "PUBLIC: /api/${COL} — ${COUNT} records"
  fi
done

# Check if user registration is publicly enabled
REG_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -X POST "https://example.com/api/auth/local/register" \
  -H "Content-Type: application/json" \
  -d '{"username":"test","email":"test@test.com","password":"Test1234!"}' \
  2>/dev/null)
if [ "$REG_STATUS" == "200" ]; then
  echo "PUBLIC REGISTRATION: /api/auth/local/register is open"
fi

Strapi Security Hardening

Strapi Security Hardening Checklist:
Security TestMethodRisk
All content type REST endpoints publicly accessibleGET /api/{collection} — new content types publicly accessible until permissions explicitly configuredHigh
CVE-2023-22621 SSTI via password reset email template (Strapi < 4.5.6)POST /api/auth/forgot-password with template injection payload in email field — RCE via Lodash template renderingCritical
Public user registration enabledPOST /api/auth/local/register — registers new user account without admin approval; enables access to authenticated endpointsHigh
GraphQL introspection exposes complete schemaPOST /graphql with introspection query — reveals all content types, field names, and relationshipsMedium
Admin panel accessible from internetGET /admin returns login page — brute force or credential stuffing possible; admin panel should be IP-restrictedHigh
File upload accepts arbitrary MIME typesPOST /api/upload with HTML/PHP file disguised as image — server-side file of arbitrary type storedHigh

Automate Strapi Security Testing

Ironimo tests Strapi deployments for publicly accessible REST API endpoints for all content types that have not had permissions explicitly configured, CVE-2023-22621 Server-Side Template Injection via the password reset email system in versions prior to 4.5.6, public user registration at /api/auth/local/register enabling mass account creation, GraphQL introspection revealing the complete content schema without authentication, admin panel at /admin accessible from the internet without IP restriction, and file upload endpoints accepting arbitrary MIME types beyond the configured allowed list.

Start free scan