Skip to content

017 Guest question submission bypasses `allowQuestionsForGuests=false` when ask-questions feature is enabled

Low
thorsten published GHSA-546h-9ghq-x49g Aug 20, 2026

Package

No package listed

Affected versions

<= 4.1.7

Patched versions

4.1.8

Description

Summary

The authorization helper QuestionController::isAddingQuestionsAllowed() returns true for unauthenticated callers whenever the global feature toggle main.enableAskQuestions is enabled, completely ignoring the separate records.allowQuestionsForGuests setting. This allows unauthenticated users to submit questions via the question/create API endpoint even when the administrator has explicitly disabled guest submissions.

Details

In phpmyfaq/src/phpMyFAQ/Controller/Frontend/Api/QuestionController.php, the private isAddingQuestionsAllowed() method contains a logic flaw where two independent configuration checks both independently grant full access:

private function isAddingQuestionsAllowed(): bool
{
    if ($this->configuration->get(item: 'records.allowQuestionsForGuests')) {
        return true;
    }

    if ($this->configuration->get(item: 'main.enableAskQuestions')) {
        return true;   // ← grants access to ALL callers, regardless of login state
    }

    return $this->currentUser->perm->hasPermission(
        $this->currentUser->getUserId(),
        PermissionType::QUESTION_ADD->value,
    );
}

The intended semantics of these two settings are:

  • main.enableAskQuestions — master on/off toggle for the "Ask a Question" feature.
  • records.allowQuestionsForGuests — whether unauthenticated users may use this feature.

However, the implementation treats enableAskQuestions=true as "grant access to everyone," making the guest-control setting irrelevant. When the feature is enabled (the expected state for any deployment using the feature), the permission check on the final return statement is dead code — it is only reached when the feature is disabled entirely.

The controller proceeds to persist the question and trigger admin notifications after this check:

$this->question->add($questionEntity);
$this->notification->sendQuestionSuccessMail($questionEntity, $categories);

Note on severity: This is a borderline finding. The practical impact is limited to unauthorized data creation in the question moderation queue. An attacker cannot read, modify, or delete existing data, and submitted questions still go through the admin review workflow. However, it represents a clear contradiction of the admin's configured intent and could be abused for spam injection.

Suggested fix — restructure the method so the feature toggle gates the entire feature, login state determines whether the guest setting applies, and authenticated users are checked for the QUESTION_ADD permission:

 private function isAddingQuestionsAllowed(): bool
 {
-    if ($this->configuration->get(item: 'records.allowQuestionsForGuests')) {
-        return true;
+    if (!$this->configuration->get(item: 'main.enableAskQuestions')) {
+        return false;
     }
 
-    if ($this->configuration->get(item: 'main.enableAskQuestions')) {
-        return true;
+    if (!$this->currentUser->isLoggedIn()) {
+        return (bool) $this->configuration->get(item: 'records.allowQuestionsForGuests');
     }
 
     return $this->currentUser->perm->hasPermission(
         $this->currentUser->getUserId(),
-        PermissionType::QUESTION_ADD->value,
+        PermissionType::QUESTION_ADD->value
     );
 }

PoC

Prerequisites:

  • A running phpMyFAQ instance (any supported 4.x version).
  • Admin access to configure settings.

Step 1: In the admin panel, set the following configuration:

  • main.enableAskQuestions = true (enable the "Ask a Question" feature)
  • records.allowQuestionsForGuests = false (disable guest submissions)

Step 2: Open a private/incognito browser window (no login session, no cookies).

Step 3: Send an unauthenticated POST request to the question creation endpoint:

curl -i -X POST 'https://<host>/api/question/create' \
  -H 'Content-Type: application/json' \
  --data '{"lang":"en","question":"test from guest","name":"guest","email":"guest@example.org","category":1}'

Expected result (with fix): HTTP 403 Forbidden — the unauthenticated caller is rejected because records.allowQuestionsForGuests is false.

Actual result (vulnerable): HTTP 200 OK with a success message. The question is persisted via $this->question->add(...) and an admin notification email is triggered via $this->notification->sendQuestionSuccessMail(...).

Root cause verification: You can confirm the logic flaw by reading isAddingQuestionsAllowed() in QuestionController.php — the second if branch (main.enableAskQuestions) returns true unconditionally, without checking whether the caller is authenticated. This means the records.allowQuestionsForGuests=false setting on the first branch is never relevant when the feature is enabled, because the second branch short-circuits before the permission check is reached.

Impact

This is a low-severity authorization bypass. The records.allowQuestionsForGuests configuration setting is rendered ineffective whenever the ask-questions feature is enabled, allowing unauthenticated users to inject questions into the admin moderation queue.

  • Who is impacted: Any phpMyFAQ deployment that has main.enableAskQuestions=true (the standard state for deployments using the feature) while relying on records.allowQuestionsForGuests=false to restrict question submission to authenticated users.
  • What an attacker can do: Submit arbitrary questions to the moderation queue without authentication, bypassing the admin's configured guest restriction. This could be used for spam injection or to generate unwanted admin notification emails.
  • What an attacker cannot do: Read, modify, or delete existing data. Submitted questions still enter the review queue and require admin action to be published.

Affected versions

= 4.0.0-alpha.2, <= 4.2.0-alpha (latest pre-release), and <= 4.1.7 (latest stable release)

The vulnerable authorization logic was introduced in commit 51d80d33a ("feat(api): refactored private API to add questions"), first included in version 4.0.0-alpha.2. The flaw is present in every subsequent release through 4.1.7 (latest stable) and 4.2.0-alpha (latest pre-release). The current development HEAD is also affected. Per SECURITY.md, versions 4.1.x and 4.2.x are supported for security fixes; versions < 4.1 are end of life.

Pavel Kohout
Aisle Research

Severity

Low

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N

CVE ID

No known CVE

Weaknesses

Incorrect Authorization

The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. Learn more on MITRE.

Credits