Hasura GraphQL Engine automatically generates a real-time GraphQL API on top of PostgreSQL, MySQL, SQL Server, and other databases, making it extremely popular for rapid API development. Its security model requires careful assessment: if HASURA_GRAPHQL_ADMIN_SECRET is not set, Hasura exposes the entire database schema as a GraphQL API with no authentication required — any unauthenticated request can query all tables; the admin secret, when set, should never be leaked to client applications as it bypasses all row-level security and permission rules; Hasura's permission system relies on JWT claims or webhook authentication, but the session variable headers x-hasura-role and x-hasura-user-id must be extracted from a verified JWT — if the application forwards user-controlled headers directly to Hasura, privilege escalation is possible; GraphQL introspection is enabled by default in Hasura and reveals the complete schema; event triggers and remote schema integrations create SSRF surfaces when Hasura makes outbound requests to configured webhook endpoints; and the /v1/metadata endpoint exports the entire permission configuration when admin secret is known. This guide covers systematic Hasura security assessment.
# Hasura default port: 8080 (also commonly deployed on 443/80 behind a proxy)
# Common paths: /v1/graphql, /v2/query, /healthz, /v1/version
# Check Hasura health and version
curl -s http://hasura.example.com:8080/healthz 2>/dev/null
# Returns: {"status":"OK"} if Hasura is running
curl -s http://hasura.example.com:8080/v1/version 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
print(f\"Hasura {data.get('version','?')} ({data.get('edition','?')})\")
" 2>/dev/null
# Check if Hasura console is accessible (reveals admin access)
curl -s -o /dev/null -w "%{http_code}" \
http://hasura.example.com:8080/console 2>/dev/null
# Without HASURA_GRAPHQL_ADMIN_SECRET set, all requests are admin by default
# Test by querying any table — if it returns data, no auth is configured
# Check if admin secret is required
curl -s -X POST http://hasura.example.com:8080/v1/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ __typename }"}' 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
errors = data.get('errors', [])
if errors:
for e in errors:
print(f\"Auth required: {e.get('message','?')}\")
else:
print('NO AUTH REQUIRED — Hasura accessible without admin secret')
print(f'Response: {str(data)[:200]}')
" 2>/dev/null
# If auth is not required, enumerate all tables via introspection
curl -s -X POST http://hasura.example.com:8080/v1/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "{ __schema { queryType { fields { name description } } } }"
}' 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
fields = data.get('data',{}).get('__schema',{}).get('queryType',{}).get('fields',[])
print(f'Query fields: {len(fields)}')
# Each field corresponds to a database table
for f in fields[:20]:
print(f\" {f.get('name','?')}\")
if len(fields) > 20:
print(f' ... and {len(fields)-20} more')
" 2>/dev/null
# Hasura trusts x-hasura-role and x-hasura-user-id headers when passed with a valid JWT
# Vulnerability: some integrations forward user-controlled headers directly to Hasura
# OR: JWT validation is weak allowing forged tokens
# Test if x-hasura-role: admin is trusted without a valid JWT
# (This should be blocked — Hasura should require admin secret for admin role without JWT)
curl -s -X POST http://hasura.example.com:8080/v1/graphql \
-H "Content-Type: application/json" \
-H "x-hasura-role: admin" \
-H "x-hasura-user-id: 1" \
-d '{"query": "{ __typename }"}' 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
errors = data.get('errors', [])
if not errors:
print('VULNERABILITY: x-hasura-role header accepted without admin secret or JWT')
else:
print('Headers correctly rejected without valid auth')
" 2>/dev/null
# Test JWT with injected role claims (if using JWT auth mode)
# A valid JWT with x-hasura-role: admin in claims bypasses all permissions
# This tests whether the application generates JWTs with insufficient claim validation
HASURA_GRAPHQL_DISABLE_INTROSPECTION=true to prevent schema enumeration; this is critical for production deploymentsHASURA_GRAPHQL_ENABLE_CONSOLE=false and HASURA_GRAPHQL_DEV_MODE=false to disable the console and developer features in production| Security Test | Method | Risk |
|---|---|---|
| Admin secret not configured | POST /v1/graphql without credentials — 200 with data means full database access without auth | Critical |
| Admin secret leaked in client application | Review JS bundles, mobile apps, browser devtools — admin secret grants full database bypass | Critical |
| Introspection reveals full schema | POST __schema query — enumerates all tables, relationships, and permission-restricted fields | High |
| x-hasura-role header injection | Send x-hasura-role: admin without admin secret — tests if role bypass is possible | High |
| Excessive permissions per role | Authenticated query as non-admin role — test what tables and operations are accessible vs expected | High |
| Metadata API exposes permission rules | POST /v1/metadata with admin secret — exports all table permissions and RLS configuration | Medium |
Ironimo tests Hasura deployments for missing admin secret giving full unauthenticated database access, GraphQL introspection enabled in production revealing complete schema and table structure, x-hasura-role header injection bypassing row-level security permissions, console and developer mode accessible in production environments, event trigger webhook endpoints reachable from the internet enabling SSRF, and admin secret exposure in client application bundles or API responses.
Start free scan