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.
# 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
| Security Test | Method | Risk |
|---|---|---|
| All content type REST endpoints publicly accessible | GET /api/{collection} — new content types publicly accessible until permissions explicitly configured | High |
| 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 rendering | Critical |
| Public user registration enabled | POST /api/auth/local/register — registers new user account without admin approval; enables access to authenticated endpoints | High |
| GraphQL introspection exposes complete schema | POST /graphql with introspection query — reveals all content types, field names, and relationships | Medium |
| Admin panel accessible from internet | GET /admin returns login page — brute force or credential stuffing possible; admin panel should be IP-restricted | High |
| File upload accepts arbitrary MIME types | POST /api/upload with HTML/PHP file disguised as image — server-side file of arbitrary type stored | High |
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