Summary
A default empty API client token allows any unauthenticated user to create and modify FAQ entries, categories, and questions via the REST API. The vulnerability exists in all versions since API v4.0 was introduced because the installation process seeds api.apiClientToken with an empty string, and the hasValidToken() comparison logic cannot distinguish between "no token configured" and "attacker sent a matching empty token header."
Details
The root cause is in two files:
1. Installation default (src/phpMyFAQ/Setup/Installation/DefaultDataSeeder.php, line 277-278):
'api.enableAccess' => 'true',
'api.apiClientToken' => '', // ← defaults to empty string
2. Authentication check (src/phpMyFAQ/Controller/AbstractController.php, line 198-204):
protected function hasValidToken(): void
{
$request = Request::createFromGlobals();
if ($this->configuration->get('api.apiClientToken') !== $request->headers->get('x-pmf-token')) {
throw new UnauthorizedHttpException('"x-pmf-token" is not valid.');
}
}
The method uses strict inequality (!==). When api.apiClientToken is '' (default) and the attacker sends x-pmf-token: (empty header value), the comparison becomes '' !== '' which evaluates to false — no exception is thrown, and authentication is completely bypassed.
The OpenAPI annotations confirm the developer intended these endpoints to require authentication: write endpoints are tagged 'Endpoints with Authentication' and document HTTP 401 responses, while read-only endpoints are tagged 'Public Endpoints'.
The following API endpoints call $this->hasValidToken() as their only authentication check:
| File | Endpoint | Method |
| ------------------------------------------------------- | ---------------------- | ------ |
| | | |
| | | |
| | | |
| | | |