Skip to main content
Application-layer security
セキュリティ
脆弱性診断
OWASP
発注
Supabase

Can You Run a Security Assessment With Only a Production Environment? — Splitting Zero-Touch Review, Read-Only Checks, and Tests That Need Staging

No staging, only production? You can still run a security assessment. Split methods into review that never touches production, read-only checks against it, and tests that need staging or written agreement — with a decision table, a rules-of-engagement checklist, safe test accounts, and why backups are no undo button.

Published
Reading time
17 min read
Author
友田 陽大
Share

The short answer: yes. Even with only a production environment and no staging (a separate pre-production environment for testing), you can run a security assessment. The key is not to decide "may we test production?" once for everything, but to split the work method by method — does it touch production, and if so, does it write?

  • Review that never touches production: read the source code, configuration, dependencies, and the database schema and access-control policies.
  • Read-only checks against production: passive scanning that does not attack, header and TLS checks, and read-only authorization checks with test accounts.
  • Tests that need staging or a written agreement: active scanning, fuzzing, load testing, and anything that writes data.

For a small company or a startup, "we only have production" is perfectly normal. It is not a good reason to put off an assessment. This post turns the three-way split into a decision table, then covers what to put in writing before you send anything at production, how to create test accounts, why backups are not something to lean on, and when it is cheaper to just stand up a test environment.


Decision table: split methods by whether they touch production

The impact on production varies widely by method, so split them in a table first and pick only what you need. Rows near the top have the least impact; rows near the bottom need the most preparation.

MethodTouches production?Main riskWhat you need
Source code reviewNoPractically none (only managing who sees the code)Read access to the repository
Configuration review (hosting, auth, env var names)No (only reading settings screens)Handing out overly broad admin permissionsA view-only role, or exported screenshots
Dependency audit (npm audit etc.)NoPractically noneThe lockfile
DB schema and policy reviewNo (reading migrations)Practically none; misses drift from productionMigrations, or a data-free schema dump
Passive scanning (ZAP baseline)Read-onlyThe spider hits a GET that changes state / extra trafficAgreed target URLs and time window
Header and TLS checksRead-onlyPractically none (a handful of requests)Target URLs
Authorization checks with test accounts (read-only)Read-onlyActually seeing another customer's dataTest accounts and a test tenant, written agreement
Active scanning (ZAP full scan etc.)Yes, including writesJunk data, degraded performance, notifications sent by mistakeStaging, or written agreement plus stop conditions
FuzzingYes, including writesErrors or data corruption from unexpected inputStaging as the rule
Load testingYesOutage, spikes in usage-based billingStaging as the rule
Tests that write data (privilege escalation, tampering)WritesChanged customer data, side effects you cannot undoStaging, or backups plus written agreement

This split follows the reasoning of the US National Institute of Standards and Technology's testing guide, SP 800-115. When selecting techniques, it says organizations should consider whether testing should be performed on production systems or on similarly configured non-production systems, and lists the factors to weigh: the possible impact on production (a technique likely to cause a denial of service should probably run against a non-production system), the presence of sensitive personal data (use test data instead of real data), and how similarly production and non-production can be configured. That last factor is easy to overlook. The guide itself notes that test and production environments usually differ in practice, and that testing outside production can miss vulnerabilities. So "we only have production" is a limitation, but it is also an advantage: the environment you examine is the one you actually need to protect.


Before you hold real customer data

Web app & SaaS security audit — authorization, tenant isolation and config, verified in the code

① How far does review that never touches production go?

Many serious flaws can be found without sending a single request to production. Broken authorization in particular — who is allowed to see which data — is the kind of problem you find by reading code and policies.

Here is what such a review reads:

  • Source code: whether APIs and server-side handlers check that an ID in the request (URL parameters, submitted data) belongs to the signed-in user before passing it to the database, and whether admin-only operations actually check for admin.
  • Database schema and access-control policies: on Supabase, whether the SQL in supabase/migrations defines RLS (row-level access control) for every table, whether the conditions narrow rows to the owner or tenant, and whether writes are constrained. If you suspect production has drifted from the migrations, a schema-only dump with no data is enough.
  • Configuration: the list of hosting environment variable names (no values needed), auth settings (email confirmation, password rules, redirect targets for external sign-in), and storage visibility settings.
  • Dependencies: known vulnerabilities, checked against the lockfile.

The thing to stress is that because this stage never touches production, it does not directly confirm production's actual state. If someone changed a policy by hand in the dashboard and it never made it into a migration, the code alone will not tell you. So treat the results of a review as a hypothesis — "with this design, production should behave like this" — and back up only the parts that need it with the read-only checks in ②.

For a Next.js × Supabase stack, where tools are enough and where a human has to read is covered in detail in What Does a Security Audit Actually Look At — Where Automation Is Enough, and Where an Audit Is Required.


② Checks that touch production read-only

As long as the requests do not change data, you can check production too. But "read-only" does not automatically mean "harmless", so here are the caveats for each method.

Passive scanning: no attacks, but the spider still runs

According to its official documentation, the OWASP ZAP baseline scan runs the ZAP spider against the target for 1 minute by default, then waits for passive scanning to complete before reporting. It performs no actual attacks, so it finishes in a few minutes at most, and the docs say it is intended to be ideal for CI/CD, even against production sites. What it finds are problems visible just by observing responses: missing security headers, cookie attributes, information leaks.

There is one caveat. The spider follows the links on each page and sends requests. In its own description of modes, ZAP lists spidering among "potentially dangerous" operations, alongside active scanning and fuzzing. If your app performs a logout, a deletion, or an email unsubscribe on a GET request, the spider will trigger it. If you run it against production, narrow it down:

  • Agree on the target URLs, and state explicitly which paths are out of scope (admin screens, redirects to external services, and so on).
  • Keep authenticated crawling separate from the authorization checks below, and only do it after deciding it is actually needed.
  • Keep the spider duration at its short default, and run it during low-traffic hours.

The counterpart is ZAP's full scan. Its official docs state plainly that it "does perform actual 'attacks' and can potentially run for a long period of time" — not something to fire at production casually (covered in ③). For how to use ZAP itself, see How to Do Web-App Vulnerability Assessment — a practical guide with OWASP's methods, ZAP, and SAST.

Header and TLS checks

Checking response headers (Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, and so on) and the certificate and TLS configuration takes a handful of requests and has almost no impact on production. These are the items most reliably checked in production itself, because CDN or hosting settings can make the actual response differ from the headers you wrote in code.

Authorization checks with test accounts (read-only)

When you check for broken authorization (being able to see someone else's data) in production, target only test data you created yourself. For IDOR (flaws where changing an ID reaches someone else's data), the OWASP Web Security Testing Guide says the best approach is to have at least two (often more) users, each owning different objects, and try to reach one user's objects from the other.

In production, three rules apply:

  1. Do not guess IDs or walk through numbers. If you reach another customer's data, that is itself a data leak. Use only the IDs of data you created under test account B.
  2. Stick to read requests (list and detail views). Do not try update or delete APIs unless they are covered by the written agreement.
  3. Keep personal data out of your records. If anything other than test data ever comes back, stop on the spot, do not save the content, and report it (write this into the stop conditions).

How to create the accounts is covered in a later section.


③ Tests that need staging or a written agreement

Any test that writes data, sends a large volume of requests, or throws unexpected input is not something to run casually in production. If you run it at all, either prepare a staging environment or agree in writing on targets, time window, and stop conditions.

  • Active scanning: sends attack strings into input fields and parameters. When forms get submitted, you may receive a flood of inquiry emails, or end up with dummy orders and accounts.
  • Fuzzing: sends large amounts of unexpected input. Where exception handling is weak, errors can cascade and data can be corrupted.
  • Load testing: besides the risk of an outage, bills for usage-based services (serverless functions, databases, email sending, external APIs) can spike.
  • Authorization checks that write: "Can I update someone else's data?" or "Can a regular user escalate to admin?" sometimes cannot be confirmed without actually writing. By the time the test succeeds, production data has changed.

NIST SP 800-115 likewise says techniques such as penetration testing could lead to loss of availability or exposure of sensitive data, and that a technique likely to cause a denial of service should probably be used against a non-production system. If you must run it in production, restrict it, as the guide recommends, to hours when it least affects operations.

That said, write tests are usually only needed to confirm spots that ① and ② have already flagged as suspicious. There is no need to try everything in production from the start.


What to put in writing before you touch production

For any assessment that sends requests to production, write down what, when, from where, and how far before anything is sent, and have the accountable people on both sides confirm it. NIST SP 800-115 provides a template for this document in an appendix, calling it the Rules of Engagement (ROE). Adapted for a small web app, its items look like this:

  • Target URLs and APIs: the domains and paths that may be tested. Also list what is out of scope (the exclude list). Admin screens on the same domain and external payment or email services are usually out of scope.
  • Date and time window: start and end. Pick low-traffic hours.
  • Source IP addresses: where the tester will send requests from. You need this so monitoring and logs can tell the assessment apart from a real attack.
  • Test accounts: the list of accounts and tenants, and each one's role.
  • Permitted and prohibited actions: "read-only", "no form submissions", "no file uploads", and so on. List the tools that may be used too.
  • Stop conditions: a rising error rate, slow responses, any non-test data becoming visible. Include how testing resumes after a stop.
  • Contacts if something goes wrong: the people on both sides, and a channel that works at night and on weekends.
  • Data handling: where information from the assessment (vulnerabilities found, recorded responses) is stored, who may see it, and when it is destroyed. NIST SP 800-115 asks that test results be treated as sensitive, since they identify vulnerabilities an adversary could exploit.
  • Reporting format and timing: is a critical finding reported immediately, or bundled into the final report?
  • Signatures: the guide asks that, at a minimum, the test team leader and the organization's senior management sign to show they understand the scope and boundaries.

Even when your own developers are checking your own app, it is worth writing a short version of this list. If there is a record of who sent what and when, then when an incident happens you can quickly tell whether it was the assessment or a real attack. When you engage an outside party, testing without clear permission from whoever manages the system can be treated as unauthorized access. Your cloud providers may also have their own policies on penetration testing, so check those as well.


Creating test accounts and a test tenant safely

Create test accounts through the same flow as real customers, make them distinguishable from real customers, and disable them when you are done. Accounts created by editing the admin console or the database directly end up in a different state from real users, which undermines the check.

The essentials:

  • Use the same sign-up flow as real users. If sign-up is invite-only, start from an invite. That way you test the registration flow at the same time.
  • Use email addresses the company controls. Not the tester's personal address, but addresses you can shut off afterwards.
  • Prepare the minimum combination needed to check authorization. Two regular users in one tenant and one in another. If you have roles (admin, editor, viewer, and so on), one user per role you want to check. This applies WSTG's "at least two users" to both the tenant boundary and the role boundary.
  • Put in test data only. Do not copy real customer names or contact details; use values that are obviously fictitious.
  • Keep a list. Record the IDs of the accounts and tenants you created in the written list, and exclude them from analytics, billing, and email campaigns, so test data does not leak into revenue or user counts.
  • Do not hand admin accounts to the tester. If you need to check what an admin can do, create a dedicated account for that and use it for a limited period.
  • Disable them when you are done. On the day the assessment ends, disable the accounts, delete the test data, and record that you did.

Note that creating test accounts is itself a write to production. But it is the same operation your real users perform every day, carried out under your control — entirely different in nature from a testing tool performing writes nobody can predict.


Before any write test: backups do not guarantee "we can just roll back"

A backup is a last-resort safety net, not a way to undo only the data a test wrote. If you run write tests in production, confirm that backups exist and, before anything else, understand what you would lose if you had to restore.

Taking Supabase as the example, its official documentation says:

  • Projects on paid plans are backed up automatically every day. For finer-grained recovery, Point-in-Time Recovery (PITR: restore to any point with up to seconds of granularity) can be enabled as a paid add-on. Free-plan projects are advised to export regularly with the CLI's db dump command and keep off-site backups.
  • The project is inaccessible while a restore runs, and the downtime grows with the size of the database.
  • Database backups do not include the files you store in Storage. The database only holds metadata about those files.

What this tells you is that a restore rolls the whole project back to an earlier point. If real customers signed up or placed orders after the test started, those disappear too when you roll back. So before running write tests in production, confirm at least the following:

  1. How far back the latest backup (or PITR) lets you restore.
  2. How long a restore is expected to take, and whether everyone involved accepts the service being down during that time.
  3. Whether there is a procedure for deleting the test data individually, rather than restoring.

The third one is what really matters. Treat restoring from backup as the last resort for when individual deletion fails.


Rate limits and WAFs: block the assessment's traffic, or let it through?

If you have rate limiting or a WAF (a layer that blocks malicious-looking requests), decide whether to let the assessment's traffic through based on what you want to learn, and write that decision down. Which choice is right depends on the question you are asking.

  • To see flaws in the application itself: add only the tester's source IP addresses to the allowlist, and only for the duration of the assessment. If the WAF keeps blocking, testing stops in front of the WAF and you never see the flaws left in the app. A WAF is just one layer of defense and can be bypassed, so you should know about the app's own flaws.
  • To see your actual defensive posture: keep the WAF and rate limits on. This shows you what an attacker sees.

If you allowlist, do not switch off the WAF or the rate limiting itself, limit the exception to the tester's IPs and the agreed period, and record that you removed it afterwards. A temporary exception that someone forgets to remove is a common accident.

Passive scans and read-only checks also add requests. Agree in advance on spacing out the tool's requests so that rate limiting does not end up blocking legitimate users too.


Sometimes "just spin up staging" is the cheaper answer

If you need write tests, standing up a test environment just for the assessment and deleting it afterwards can be cheaper than running those tests carefully in production. Think of "keeping an environment forever" and "standing one up temporarily" as two different things.

On a Supabase and Vercel stack, the options are:

  • Supabase branches: per the official docs, each branch is a separate environment with its own Supabase instance and API credentials, built by applying your production migrations. By default, a new branch starts with none of your production data or storage objects; you populate it with a seed file. Preview branches are deleted automatically when the pull request is merged or closed.
  • Vercel preview deployments: Vercel provides a Preview environment alongside Production out of the box, and pushing to a branch other than the production branch creates a preview deployment. If the preview environment variables point at the branch's Supabase instance, you have a test environment disconnected from production. Conversely, if the preview environment variables still point at the production database, "testing on preview" is writing to production. Always check where those variables point before testing.
  • A local environment: Supabase's local development setup runs the database, auth, and the rest on your machine with Docker. For checks that never need to be exposed publicly, that may be all you need.

Costs depend on your plan and how long things run, so this post does not quote figures. What you should compare is the cost of a temporary environment against the preparation needed to run write tests in production (written agreement, backup checks, an individual-deletion procedure, someone on standby) plus the loss if something goes wrong. If your migrations live in the repository, a test environment is easy to reproduce and the comparison tends to favor standing one up. On the other hand, for an app whose tables were created by hand in the dashboard with no migrations, building an environment identical to production is a major job in itself. In that case, exporting the schema as migrations is an improvement worth making before any assessment.

Even when you test in a separate environment, as SP 800-115 points out, differences from production can make you miss vulnerabilities. Cross-check results from the test environment against production's settings (environment variable names, auth settings, storage visibility).


Summary: with only production, go in this order

To recap:

  • You can run an assessment with only production. Split methods into "never touches production", "touches it read-only", and "needs staging or a written agreement", and the scope you can cover without breaking production becomes clear.
  • Start with review that never touches production. Reading code, configuration, dependencies, and the database schema and policies is enough to find many serious problems, including broken authorization.
  • Keep read-only checks against production narrowly scoped. Passive scanning does not attack, but the spider still runs. Authorization checks target only test data you created yourself.
  • Put the rules of engagement in writing before sending requests to production. Targets, time window, source IPs, test accounts, stop conditions, contacts, data handling, signatures.
  • Write tests go to staging or happen under written agreement. Restoring from backup also erases customers' writes, so "we can just roll back" does not hold. Temporarily spinning up a test environment can be the cheaper path.

If you want an outside party to assess an app that only has production, you can talk to me about a security assessment and audit that starts with review that never touches production. How far the work touches production is agreed in writing before anything begins. If you are on Next.js and Supabase and want to go deep on RLS and tenant isolation, the Next.js × Supabase specialist security audit is the better fit.

Frequently asked questions

We have no staging environment, only production. Can we still get a security assessment?
Yes. Reviewing source code, configuration, dependencies, and the database schema and access-control policies sends no requests to production at all. Where production does need to be checked, restricting yourself to passive scanning that does not attack, and to read-only checks with dedicated test accounts, lets you proceed without changing any data. Only active scanning and tests that write data require staging or a written agreement.
Is the OWASP ZAP baseline scan safe to run against production?
It does not attack, but that does not make it harmless. The official docs describe it as running the spider for 1 minute by default and then waiting for passive scanning to finish, and say it is intended to be usable even against production sites. But the spider follows links and sends requests, so if logout or deletion runs on a GET, it will fire. Agree on target URLs and a time window, and avoid authenticated crawling.
What should we put in writing before testing production?
At minimum: the target URLs (and those out of scope), the date and time window, the tester's source IP addresses, the test accounts to be used, permitted and prohibited actions, stop conditions, contacts if something goes wrong, and how the resulting data is handled and destroyed. NIST SP 800-115 frames this document as the Rules of Engagement and asks the accountable people on both sides to sign it.
If we have backups, is it fine to run write tests in production?
Not really. A Supabase restore rolls the whole project back to an earlier point; the project is inaccessible while it runs, and any data customers wrote after that point is lost too. Database backups also do not include the files stored in Storage. A backup is a last-resort safety net, not a way to undo only the test data.
Isn't building a staging environment just for an assessment overkill?
If you need write tests, it can actually be the cheaper option. A Supabase branch builds a separate environment from your production migrations and, by default, brings over none of your production data. Paired with Vercel preview deployments, you can stand an environment up for the duration of the test and delete it afterwards. Weigh that against the effort and accident risk of running write tests carefully in production.

References

友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

Before you hold real customer data

Web app & SaaS security audit — authorization, tenant isolation and config, verified in the code

Beyond what tools catch, the design gap — who may touch which data — checked in the source code and configuration, with a prioritized fix plan. Not only Next.js / Supabase: web apps on Rails, Laravel, Django, Go and others, plus AWS / GCP / Vercel configuration. We never call anything “completely secure.”

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

Also worth reading