Allow PUT and DELETE only against an environment you can reset, with test data and test accounts. Active API scans use write methods because authorization flaws often hide there. Against shared or production data, run a passive or read-only pass instead, and test write paths by hand with owned objects.
Why scanners send write methods
A read-only scan can tell you a GET endpoint returns the right status code. It cannot tell you whether a normal user can change or delete another user's record. OWASP's guidance on testing for broken object level authorization says to try each HTTP method against another account's object: GET to read, POST, PUT or PATCH to modify, and DELETE to remove. The same applies to function level checks, where a low-privilege user tries to change a role or remove a resource.
So if a scan never sends write methods, it skips the checks that matter most for authorization.
What can go wrong
- Real records get changed or removed.
- Test objects pile up and slow later runs.
- A DELETE on a collection URL removes more than intended if the API allows it.
- Side effects fire: emails, webhooks, payment calls, ticket creation.
Side effects are the one people forget. An API that sends a confirmation email on every create will send hundreds during a scan unless the environment points at a sandbox mail service.
Three ways to run it
| Mode | What it does | Use when |
|---|---|---|
| Passive or baseline only | Observes traffic and responses, no attack requests | Production, or a first look at an unknown API |
| Active with an allow list | Sends attack requests but only to listed methods and paths | Shared staging with some real-looking data |
| Full active | Sends all methods to all imported operations | A disposable environment rebuilt from seed data |
The open-source ZAP API scan script has a safe mode flag that skips the active scan and performs a baseline scan, which means passive checks only. Its documentation also says rules set to IGNORE in the config are not run during active scanning, so you can turn off individual checks that are too disruptive for your target.
A setup that holds up
- Seed a clean environment with synthetic data, so nothing real is at risk.
- Create test users and objects the scan is allowed to change.
- Point email, payment and messaging integrations at sandboxes.
- Exclude paths that trigger irreversible actions, such as account closure or bulk export.
- Snapshot or script a reset, and run it after every scan.
- Confirm the API itself uses a method allow list and returns 405 for anything else. The OWASP REST security cheat sheet recommends exactly that.
What to do with production
Do not give up on write-path testing, but change how you do it. Have a person test a small, agreed set of operations using objects created for the purpose, and watch the logs while they do. Keep the scanner passive.
Worked example: a notes API with three write routes
Consider a notes service with these routes: list notes, read a note, create a note, update a note, and delete a note. There is also an admin route that deletes every note belonging to a user. A team wants to scan it in a way that finds authorization bugs without wrecking anything. This is how they could decide, route by route.
| Route | Method | Risk if a scanner hits it | Decision |
|---|---|---|---|
| List notes | GET | None, read only | Allow in every mode |
| Read a note | GET | None, read only | Allow in every mode |
| Create a note | POST | Adds records, may trigger a notification | Allow in test only, with the notification stubbed |
| Update a note | PUT | Overwrites data | Allow in test only, on seeded notes |
| Delete a note | DELETE | Removes data | Allow in test only, on seeded notes, and reset after |
| Delete all notes for a user | DELETE | Removes many records at once | Exclude from the scan, test by hand with one disposable user |
The last row matters most. A route that removes many records belongs in a short manual test, where a person controls exactly which user is affected, rather than in an automated run that sends variations at it.
Seeding. The team creates two users, Ada and Ben, each with ten notes. Ada's note IDs are saved in a private file. The scan logs in as Ben and tries Ada's IDs with each allowed method. The expected result for every attempt is a 403 or 404. A 200 on a PUT or DELETE is a serious finding, because Ben has changed or removed Ada's data.
Reset. After the run, a script drops all notes and reseeds both users. If a vulnerable route did delete Ada's notes, the damage lasts until the reset and no longer.
Side effects. The create route sends an email through a notification service. In the test environment the service points at a sandbox that discards messages, so a thousand creates produce no mail.
What the report should show. Which methods were allowed, which routes were excluded and why, and the result for each account. If the report says only "scan completed", the team cannot tell whether write methods were tested at all.
Reading the outcome
When all attempts by Ben return 403 or 404, you have decent evidence that ownership checks exist on those routes. When some return 200, you have a ranked list of fixes. In either case the manual test of the bulk delete route gives you a view of the one route the scanner never touched.
This pattern scales to larger APIs: sort routes into read only, bounded write, and bulk or irreversible, and treat each group differently.
Questions to settle with the team first
Before the first run, get agreement on four things, in writing, so nobody argues about them after something breaks.
- Which environment is the target? Name it, and confirm how it is rebuilt. If nobody can say how to reset it, it is not ready for write methods.
- Which data is in it? If any record came from production, either remove it or treat the environment like production. The OWASP inventory guidance says to avoid production data in non-production deployments, and to apply production-grade controls if you cannot.
- Which actions are irreversible? Account closure, bulk export, outbound payments. List them and exclude them.
- Who watches the run? Someone should be able to pause it, and should know what normal looks like in the logs.
A short run with these answered is worth more than a long run without them.
Try it yourself
Open the demo below and look at which requests the example scan sends and how findings are grouped. Think about which of those you would be comfortable sending to your own environment.
Click through it at your own pace, or open it full screen.
Quick check
Is a passive scan useless for authorization bugs?
Mostly, yes. Passive checks catch headers, cookies and some information leaks, but broken authorization needs requests made as different users.
Can a gateway block a scanner from deleting things?
It can reduce the damage, but then the scan stops testing what your application would do. Better to isolate the environment.
Should DELETE be tested at all if the data is valuable?
Yes, but against seeded test data, never the valuable records. The point is to learn whether a user can delete something that is not theirs. A seeded environment lets you test that safely.
Sources
- OWASP WSTG: API broken object level authorization
- OWASP WSTG: API broken function level authorization
- OWASP REST Security Cheat Sheet
- ZAP API scan documentation
Last reviewed: 19 October 2026.