dotCMS / dotCMS/core

security: CONTENT_APIS_ALLOW_ANONYMOUS defaults to READ, exposing content API to unauthenticated access

Open
#34,997 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stale
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Summary

On DotCMS Cloud (and any deployment behind a CDN/WAF), the origin server hostname can be discovered via SAML issuer URL leakage. Once the origin is known, an attacker can bypass CloudFront and DataDome entirely and query the content API directly — because `CONTENT_APIS_ALLOW_ANONYMOUS` defaults to `READ`, which is the correct setting for public-facing content but assumes the origin is only reachable via trusted infrastructure.

Note: Setting `CONTENT_APIS_ALLOW_ANONYMOUS=NONE` is not the right fix here — it would break any public-facing page that uses the content API to render content without a logged-in user. The default of `READ` is intentional for headless CMS use cases.

Root Cause

The vulnerability is not in the `CONTENT_APIS_ALLOW_ANONYMOUS` default itself, but in:

  1. Origin hostname exposure — the SAML issuer URL leaks `esa-prod.dotcms.cloud` (or equivalent), allowing the CDN/WAF layer to be bypassed entirely
  2. No origin-level access restriction — the origin server accepts traffic from any IP, not just CloudFront ranges
  3. UserProxy plugin over-permissive configuration — if the `com.dotcms.userproxy` plugin is installed with broad URL patterns (e.g., `/api/v1/.*`), requests to those patterns are auto-authenticated as a service account, granting access beyond anonymous content

Attack Scenario

  1. Attacker inspects the franchise site SAML login flow → discovers `esa-prod.dotcms.cloud` from the issuer URL
  2. Attacker queries `GET /api/v1/content/{id}` directly on the origin — no credentials needed
  3. CloudFront, DataDome, and WAF rules are bypassed entirely
  4. If UserProxy plugin is installed with `/api/v1/.*` patterns, requests are auto-authenticated as a service account → access beyond anonymous level

Files Involved

  • `dotCMS/src/main/java/com/dotcms/rest/AnonymousAccess.java` (line 38) — default `READ`
  • `dotCMS/src/main/java/com/dotcms/rest/api/v1/content/ContentResource.java` (lines 410, 453, 487, 560, 898) — `AnonymousAccess.READ` on GET endpoints
  • `dotCMS/src/main/java/com/dotcms/rest/WebResource.java` (line 553) — anonymous user passthrough

Proposed Fix

1. Infrastructure — Highest Priority (Cloud Ops)

Restrict the origin server to only accept inbound HTTP/HTTPS traffic from CloudFront IP ranges. Block all other sources at the security group / firewall level. This fix makes the origin unreachable to attackers regardless of whether they know the hostname.

2. SAML Issuer URL (Application)

Audit whether the SAML issuer URL must contain the origin hostname. If not, replace it with the CDN/public hostname so the origin is never leaked through the login flow.

3. UserProxy Plugin — Tighten Configuration

The `com.dotcms.userproxy` plugin is the correct mechanism for selectively opening specific API endpoints to non-authenticated callers. However, broad patterns like `/api/v1/.*` should be avoided. Recommended approach:

  • Use the minimum required URL patterns (specific paths only, not wildcards over all of `/api/`)
  • Enable IP-based token validation in the plugin config to restrict access to known trusted origins (Next.js server, CDN edge IPs, etc.)
  • Avoid mapping patterns that include admin-adjacent endpoints
4. Documentation (Hardening Guide)

Add a security hardening guide entry specifically for Cloud/CDN deployments:

  • Origin server must only be reachable from the CDN edge — never directly from the internet
  • `CONTENT_APIS_ALLOW_ANONYMOUS=READ` is correct for public content; `NONE` is for fully private/authenticated instances
  • UserProxy plugin should be used for selective per-path authentication, not as a substitute for origin network restriction

What NOT to Do

  • ❌ Do not set `CONTENT_APIS_ALLOW_ANONYMOUS=NONE` on instances serving public content — this will break public-facing pages
  • ❌ Do not rely on DataDome/CloudFront as the sole auth gate without restricting origin access
  • ❌ Do not configure UserProxy with `/api/v1/.*` wildcards on an internet-accessible origin

Acceptance Criteria

  • Origin server network rules restrict inbound traffic to CloudFront IP ranges only
  • SAML issuer URL no longer exposes the origin hostname
  • UserProxy plugin configuration audited — broad API wildcards replaced with specific paths + IP restrictions
  • Security hardening guide updated with origin restriction and UserProxy guidance for Cloud deployments
  • Internal runbook created for rotating API tokens if origin was accessed directly

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading dotCMS/src/main/java/com/dotcms/rest/AnonymousAccess.java, the listed GET endpoints in ContentResource.java, and anonymous handling in WebResource.java. Then review the SAML issuer, origin access, and UserProxy configuration requirements in the issue. Done means the accepted infrastructure, application, plugin, hardening-guide, and runbook criteria are addressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, java
Domain
api, authentication, cloud, documentation, infrastructure, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.