PRC001 — Operation has no authentication
| Severity | high |
| OWASP API Security Top 10 | API2:2023 Broken Authentication |
What it catches
An operation that can be called without credentials. That is the case when:
- neither the operation nor the spec declares a
securityrequirement; - the operation overrides the spec's requirement with
security: []; - one of the alternatives is the empty requirement
{}, which OpenAPI defines as "anonymous access is allowed" (optional authentication).
An operation that requires a credential through a parameter instead of
a security scheme, as older specs often do, is not reported: a required
header or query parameter named Authorization or like a credential
(x-access-token, X-Auth-Token, session_token, api_key). The same
applies to the rules about anonymous operations (PRC006, PRC007), and PRC010
tells such credentials apart by name. Not enough: an optional parameter, or
a bare token (/password/reset?token=, a captcha's, a device's), which is
as often a one-time link as a login; a CSRF or request token is not a
credential either. Declaring it as a security scheme is still better: tools,
gateways and generated clients understand schemes, not parameters.
Webhooks are not checked: your API calls them, nobody calls your API through them.
Why it matters
Anyone who can reach the API can call the operation without saying who they are. If it reads or changes data, that data is open to everyone. Forgetting authentication on one new operation is one of the most common ways APIs leak.
A spec that does not declare authentication may still describe an API that enforces it in code. Then the spec is wrong, and everything generated from it (clients, docs, gateways, future Procyon runtime tests) is wrong too. Fix the spec either way.
How to fix it
Declare authentication once for the whole spec, so new operations are protected by default:
security:
- bearer: []
components:
securitySchemes:
bearer:
type: http
scheme: bearer
Or add security to the operation itself.
If the operation really is meant to be public (a health check, the login operation itself), mark it as a public operation, with the reason:
paths:
/health:
get:
x-procyon-public: "Load balancer health check, returns no data"
The reason is mandatory: an empty one, or true, stops the run with an error.
Marking an operation public silences PRC001 only. Rules about what a public
operation exposes (personal data, objects by ID) still apply to it.
How to ignore it
If the finding is fine on purpose, write an x-procyon-ignore on the operation,
with the reason. The finding is still reported, under Ignored, and no
longer fails the run. See ignoring a finding.
/health:
get:
x-procyon-public: "Load balancer health check, returns no data"
x-procyon-public is the usual way: it says the operation is public on
purpose. x-procyon-ignore on the operation works too, for a temporary
exception:
/reports:
get:
x-procyon-ignore:
- rule: PRC001
reason: "Public until the auth migration"
expires: 2027-01-31
Examples
Reported (GET /orders has no requirement):
paths:
/orders:
get:
responses:
"200":
description: Orders
Reported (DELETE /orders opts out of the global requirement):
security:
- bearer: []
paths:
/orders:
delete:
security: []
Not reported (the global requirement covers GET /orders):
security:
- bearer: []
paths:
/orders:
get:
responses:
"200":
description: Orders