The Staging Server No One Password-Protected
The subdomain resolved. The page loaded instantly. There was no login prompt, no redirect to an authentication page, no error. Just the full content management interface of a staging blog, open on the public internet, logged in as a guest with read access to every post in the system — including the ones marked "Draft."
That was finding SUB-004.
The Assessment Context
The engagement was a web application security assessment of a digital asset trading platform — the kind of organization that publishes announcements with real market consequences. Product launches, security advisories, regulatory updates, executive commentary on market conditions. The timing of these announcements matters in ways it does not for most organizations.
The defined scope was broad: all subdomains of the primary domain were in-scope for enumeration. Explicit authorization covered both passive reconnaissance and active probing of discovered hosts. The assessment team was several days into coverage of the primary application when the subdomain enumeration results came back.
Discovery: Certificate Transparency Logs
The enumeration methodology started with certificate transparency. Every public TLS certificate is submitted to CT logs — a requirement for browser trust since 2018. These logs are queryable, which means anyone can retrieve a complete list of every certificate ever issued for a given base domain.
For this organization, the CT log query returned hundreds of entries. Most were expected: www, api, app, various regional and service-specific subdomains. But toward the bottom of the results, several staging-pattern entries appeared:
staging.blog.example.com
blog-staging.example.com
preview.blog.example.com
All three had certificates. All three resolved to live IP addresses.
The first two returned 404 responses — likely decommissioned environments with DNS left in place. The third one, preview.blog.example.com, returned a 200 with a full HTML page. The title tag read: [Blog Name] — Preview Environment.
What Loaded
The preview environment was running a headless CMS — a content management backend that serves structured content through an API, with a decoupled frontend rendering it into HTML. The frontend was a stripped-down version of the production blog design, clearly marked as a preview environment in the page footer.
Navigation worked. Tags worked. The search function worked. Every published post that appeared on production was visible here as well.
Then: the drafts.
The CMS's REST API had an endpoint for listing content by status:
GET /api/content/posts?status=draft
On production, this endpoint required authentication and returned an empty array for unauthenticated requests. On the staging environment, it returned the full list of unpublished posts with complete content:
json
{
"posts": [
{
"id": "post-0291",
"title": "Important Security Update: Addressing Report Submitted Through Our Disclosure Program",
"status": "draft",
"scheduledPublish": "2026-04-09T09:00:00Z",
"content": "..."
},
{
"id": "post-0287",
"title": "Upcoming Changes to Withdrawal Processing Times",
"status": "draft",
"scheduledPublish": "2026-04-11T14:00:00Z",
"content": "..."
},
{
"id": "post-0284",
"title": "Q1 2026 Platform Statistics and Roadmap Preview",
"status": "draft",
"scheduledPublish": null,
"content": "..."
}
]
}Seven draft posts. All complete. All with full body text, including one detailing a security vulnerability that had been reported, patched, and was pending coordinated disclosure — with the disclosure announcement scheduled two days out.
The Content That Should Not Have Been There
Reading draft content from a financial services platform is not like reading a pending blog post from a recipe site. The categories of exposure mattered as much as the mechanism.
Pending security disclosure. Post post-0291 contained the full text of a planned security advisory — vulnerability class, affected component, patch timeline, recommended user actions. This disclosure was scheduled for April 9th. It was April 7th. Any party reading this post had a 48-hour window with knowledge of a disclosed vulnerability before the vendor's public announcement. For a trading platform, the window between private knowledge of a security issue and its public disclosure is a known sensitive period.
Operational changes with user impact. Post post-0287 announced changes to withdrawal processing times with specific new limits and timelines. For a platform where users move significant sums, this information affects user behavior. Users with advance knowledge could schedule withdrawals to avoid the new restrictions, or communicate the change to others before it was officially announced.
Unscheduled roadmap content. Post post-0284 had no scheduled publish date — it was in an exploratory draft state, containing product roadmap details the organization had not decided to release publicly. Roadmap content from a trading platform can be material to traders making decisions about the platform's future capabilities.
Beyond the draft posts, the staging environment also exposed:
- Content revision history. The CMS API provided access to all previous drafts of published posts, including text that had been removed before publication — legal language, internal references, attributions to named individuals
- Author metadata. Posts included full author profiles with internal usernames, email addresses, and in two cases, personal phone numbers stored as author contact fields
- Scheduled publication timestamps. The exact date and time of every upcoming announcement was readable, allowing external parties to know precisely when to watch for each post
The Access Model
The staging environment had been provisioned without any access restriction at the infrastructure layer. There was no HTTP basic authentication. No IP allowlist. No VPN requirement. The environment was on the public internet with a valid TLS certificate and full DNS resolution.
The CMS itself had an authentication system — the admin interface required a login. But the content API, which served the frontend with post data, had no authentication requirement on the staging environment. On production, the API enforced authentication for draft content. On staging, it did not. This was almost certainly a configuration flag — a setting like REQUIRE_AUTH_FOR_DRAFTS=false in the staging environment's configuration, intended to simplify frontend development without needing to manage content tokens.
The staging environment had been indexed by search engines. Two of the published posts that appeared on both staging and production had staging URLs appearing in search results alongside the production URLs. The environment was not hidden.
Severity Assessment
The finding was rated High with a CVSS score of 6.5. The reasoning:
Confidentiality impact is high — the exposed content included security disclosure details, operational change announcements, and roadmap information that the organization had explicitly not released publicly.
Network access is none required — the staging environment was on the public internet with no network-layer controls.
Privileges required is none — no account, token, or credential was needed to access draft content.
The score was not Critical because the attack did not provide persistent access to systems, did not expose credentials that could escalate to other access, and did not affect the integrity or availability of production systems. The harm was informational rather than operational.
However, the regulatory dimension elevated the practical severity. A financial services organization with material non-public information exposed to anyone who knew a subdomain name faces potential compliance questions beyond the technical vulnerability itself.
Remediation
The immediate fix was straightforward: add IP allowlisting to the staging environment and enforce API authentication for draft content regardless of environment.
The longer-term recommendations addressed the systemic gap:
Make authentication a provisioning requirement, not an afterthought. Infrastructure-as-code templates for staging environments should include authentication controls by default. Adding a password should not be the thing someone has to remember to do — its absence should be the thing that fails a deployment check.
Separate staging infrastructure from the public internet. Staging environments should be deployed in private network segments accessible only through VPN or corporate network. If external access is required for client review or testing, provide it through authenticated preview links with per-session tokens, not by putting the environment on a public subdomain.
Audit CMS API authentication settings independently for each environment. Application-level settings that control authentication are often environment-specific. Staging environments should have the same or stronger authentication requirements as production for any endpoint that serves non-public content.
Stop issuing public TLS certificates for internal staging environments. Using private CAs or internal certificate management for staging hosts removes them from CT log visibility. A staging subdomain that does not appear in public CT logs is significantly harder to discover through passive reconnaissance.
Review draft content storage. Some organizations choose to stage draft content in a completely separate system from the one that serves public content. If drafts are stored in a database accessible only to authenticated CMS users and never served through the public content API, the API misconfiguration becomes irrelevant.
The Pattern Behind the Finding
This finding is not unusual. Staging environments that exist for legitimate operational reasons — content review, integration testing, frontend development — accumulate exposure over time. They start with an intention to be temporary or internal. They get DNS records for convenience. They get TLS certificates so the CMS admin interface works. The certificates appear in public logs. Subdomain enumeration finds them.
The access controls that would have prevented this finding require either active configuration (adding basic auth, setting up IP restrictions) or default-secure infrastructure templates. Neither happened here.
The organization's production environment was well-secured. Authentication was enforced, rate limiting was in place, draft content was properly gated. The care taken in production did not extend to staging, where the assumption was that no one would look.
Certificate transparency logs mean someone is always looking.