Skip to content

Require bearer token for writing API endpoints (#522)#544

Open
maltehuebner wants to merge 1 commit into
mainfrom
security/api-write-auth-522
Open

Require bearer token for writing API endpoints (#522)#544
maltehuebner wants to merge 1 commit into
mainfrom
security/api-write-auth-522

Conversation

@maltehuebner

Copy link
Copy Markdown
Contributor

Fixes the critical Broken Access Control issue: all writing REST endpoints below /api (PUT /api/station, POST /api/station/{stationCode}, PUT /api/city, POST /api/{citySlug}, PUT /api/value) were completely unauthenticated and persisted straight to the database.

Changes

  • New App\EventSubscriber\ApiWriteAuthenticationSubscriber — a single kernel.request enforcement point (priority 8, runs after routing, before the controller). It challenges every write method (POST/PUT/PATCH/DELETE) whose path is /api or starts with /api/, and leaves read requests (GET/HEAD), /api/doc, and everything outside /api untouched. Also automatically covers any future write endpoint under /api.
  • Token is read from the API_WRITE_TOKEN env var, bound in config/services.yaml, sent by clients as Authorization: Bearer <token>, and compared with hash_equals() (constant time).
  • .env: documented API_WRITE_TOKEN (empty default).
  • CLAUDE.md: updated the API and Security notes.
  • Unit test tests/EventSubscriber/ApiWriteAuthenticationSubscriberTest.php (10 cases) covering read pass-through, /api/doc, valid token, missing token (401), wrong token (403), non-bearer header, unconfigured token (fail closed), and sub-request bypass.

Behaviour

  • Fails closed: when API_WRITE_TOKEN is unset/empty, every write is rejected with 403.
  • Missing token -> 401 Unauthorized (with WWW-Authenticate: Bearer).
  • Invalid token -> 403 Forbidden.

Deliberately not implemented

  • A full symfony/security-bundle firewall with per-client credentials. The issue lists that as one option; a shared token (option 2) is the minimal, self-contained fix and is noted as the chosen approach. A firewall remains a reasonable follow-up.
  • Provider-side changes (sending the token) live in other repos (provider-* / luft-api-bundle) and are tracked there. Until they send the token, writes fail closed by design.

Verification

  • vendor/bin/phpstan analyse — no errors.
  • vendor/bin/phpunit --testsuite="Project Test Suite" — 131 tests green (includes the new subscriber test).
  • bin/console lint:container --env=prod — OK (token binding wires cleanly).

Closes #522

🤖 Generated with Claude Code

The writing REST endpoints (PUT /api/station, POST /api/station/{code},
PUT /api/city, POST /api/{citySlug}, PUT /api/value) were completely
unauthenticated, allowing anyone to create/overwrite stations, cities and
measurements.

Add App\EventSubscriber\ApiWriteAuthenticationSubscriber, a single
kernel.request enforcement point that challenges every write method
(POST/PUT/PATCH/DELETE) below /api with a shared bearer token
("Authorization: Bearer <token>"). The token is configured via the
API_WRITE_TOKEN env var and compared with hash_equals. Read endpoints
(GET/HEAD, /api/doc) stay public.

Fails closed: when API_WRITE_TOKEN is unset, all writes are rejected
(403). Missing token => 401, wrong token => 403.

Providers must be updated to send the token (tracked separately in
luft-api-bundle). A full symfony/security-bundle firewall with per-client
tokens remains a possible follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Kritisch] Schreibende API-Endpunkte ohne Authentifizierung

1 participant