Acceptable Risk Thresholds for Non-Production Systems

Acceptable Risk Thresholds for Non-Production Systems

Acceptable risk thresholds for non-production systems decide which findings on staging, QA, demo and development servers you can live with, and which ones need the same urgency as production. If you run security for a team that has dozens of test environments, you have probably seen people argue that “it’s only dev” whenever a scan turns up something ugly. This article explains how to set those thresholds sensibly and where to draw lines that nobody gets to negotiate.

The short answer is that non-production can tolerate more risk, but only along one axis. You can accept slower patching and less monitoring. You cannot accept internet exposure of sensitive services, real data, or credentials that also work in production.

Why Non-Production Needs Its Own Risk Model

Treating every staging box exactly like production sounds safe, but in practice it fails. Teams with 40 ephemeral environments and a 7-day patch SLA for all of them will miss that SLA every week, and the security team ends up as the one asking for exceptions. Once findings are routinely ignored, the ones that really matter get ignored too.

A separate threshold model gives engineers room to work and keeps attention on the findings that lead to breaches. The goal is a written policy that says, for example, that a medium-severity outdated nginx build on an isolated QA server can wait 30 days, while an exposed PostgreSQL port on the same server gets fixed today.

The Myth That Non-Production Systems Are Low-Value Targets

The most persistent misconception is that attackers don’t care about test systems. In reality they don’t know or care what you call a server. Mass scanners like Masscan and ZGrab sweep the full IPv4 space in under an hour, and a Jenkins instance on port 8080 looks the same to them whether it’s labelled “prod” or “sandbox”.

Test systems are often the softer way in. They run older builds, have debug modes switched on and share SSH keys with production deployment pipelines. Jenkins CVE-2024-23897, the arbitrary file read through the built-in CLI disclosed in January 2024, was exploited mostly against forgotten CI servers that nobody considered critical. Reading credentials.xml from a “dev” Jenkins gives an attacker the same cloud keys the production pipeline uses.

Setting Acceptable Risk Thresholds for Non-Production Systems

An experienced security engineer builds the threshold around three questions rather than around the environment label: Is the system reachable from the internet? Does it hold real data? Does it have trust relationships with production? If all three answers are no, relaxed thresholds are reasonable. If any answer is yes, that dimension falls under production rules.

A practical model that holds up in audits looks like this:

Tier A – isolated, synthetic data, no production trust: critical CVEs patched within 14 days, highs within 30, mediums at the next rebuild. Internet exposure limited to HTTPS behind SSO or an IP allowlist.
Tier B – internet-facing or holding masked production data: critical within 72 hours, high within 14 days. No databases, caches or admin panels reachable externally.
Tier C – holds real customer data or production credentials: it’s production, whatever the hostname says.

For scoring, plain CVSS isn’t enough. Add EPSS probability and check the CISA Known Exploited Vulnerabilities catalogue: a CVSS 6.5 finding listed in KEV beats a CVSS 9.1 finding with an EPSS score below 0.01. A structured approach to risk scoring for externally reachable services makes these decisions repeatable instead of a matter of opinion.

Hard Lines That Never Get Relaxed

Some findings carry zero tolerance whatever the tier. Redis on 6379, Elasticsearch on 9200, MongoDB on 27017, Docker API on 2375 and Kubernetes kubelet on 10250 should never answer an external scan. Exposed instances of these services are typically found and abused within hours, whether for ransom notes, cryptominers or data theft.

The same applies to management interfaces such as Kibana, phpMyAdmin, the Spring Boot actuator endpoints and the Symfony profiler. Debug tooling like Werkzeug’s interactive debugger effectively gives anyone who reaches it remote code execution. These belong behind a VPN or a zero-trust proxy, even when they’re up for a single afternoon.

A Realistic Scenario

Consider a typical sequence. A developer needs a client to review a feature, so they spin up a demo VM on Hetzner, open ports 3000, 5432 and 9200 in the provider firewall “just for the call”, and move on. Three weeks later the VM is still running and an external scan shows PostgreSQL 13.4 answering on 5432 with password authentication. Internal tooling showed nothing, because the VM was never added to the CMDB.

The fix itself takes five minutes. The lesson takes longer: the threshold policy only worked because something was watching the public IP ranges from the outside. Temporary openings like this need an expiry date tied to them, which is what disciplined change windows and temporary port openings are for.

Common Mistakes When Defining Thresholds

The first mistake is classifying by hostname or tag instead of by actual exposure. A server tagged “env=dev” with a public IP and a security group open to 0.0.0.0/0 is internet-facing infrastructure. Thresholds should come from what an external scan sees, not from what the Terraform module intended.

The second mistake is copying production data into staging “temporarily” for debugging. One pg_dump turns a Tier A system into a GDPR-relevant data store, and nobody updates the risk classification. Masking tools like Tonic or simple anonymisation scripts in the refresh pipeline prevent this.

The third mistake is granting exceptions with no end date. An exception that reads “accepted risk – dev only” with no owner and no review date turns into permanent exposure. Every accepted risk needs a named owner and a re-evaluation date of 90 days or less.

Edge Cases Where the Standard Advice Doesn’t Apply

In regulated environments – PCI DSS cardholder data environments, DORA-scoped financial systems, healthcare under HIPAA – non-production systems connected to the in-scope network inherit in-scope controls. Relaxed tiers usually aren’t available there at all.

On the other side, truly air-gapped lab networks with no inbound or outbound routing can tolerate considerably longer patch cycles, sometimes aligned to quarterly rebuilds. Teams under 10 people often do best with a single simple rule: nothing in non-production gets a public IP unless it sits behind Cloudflare Access, Tailscale or a similar gate.

FAQ

Should non-production systems be included in external port scans?
Yes. Anything with a public IP belongs in scope. Non-production systems often produce the most surprising findings because they change more often and get less review.

How long can a critical vulnerability stay open on a staging server?
If the vulnerable service isn’t reachable from the internet and the system holds no real data, 14 days is a defensible ceiling. If it’s externally reachable or listed in CISA KEV, apply production timelines – usually 72 hours or less.

Who should approve accepted risks in non-production?
The system owner proposes it and the security lead approves it, with a documented expiry date. Approvals without an owner and a date shouldn’t count as approvals.

Summary

Non-production can safely accept slower patching and lighter monitoring, but not exposed data services, admin panels, real customer data or shared production credentials. Classify each system by what is actually reachable from outside rather than by what it’s called, give every exception an owner and an expiry date, and check the external view regularly. When findings pile up, use a clear method to prioritize port security findings by risk level so the exposed database gets fixed before the outdated banner.