PROCYON

PRC001 — Operation has no authentication

Severityhigh
OWASP API Security Top 10API2: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 security requirement;
  • 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