Problem/Motivation

We're seeing exceptions like this on several sites for the last few days, probably somebody probing https://www.drupal.org/sa-core-2026-004

The requested URL is https://www.example.com/user/login?_format=json but we don't have a log for the request body.

According to @catch it shouldn't be possible to cause such an issue and that this should be caught sooner in the stack. The full stack trace is this:

Drupal\Core\Database\InvalidQueryException: Query condition 'users_field_data.name = leakix, x' must have an array compatible operator. in Drupal\Core\Database\Query\Condition->condition() (Zeile 113 in /var/www/html/web/core/lib/Drupal/Core/Database/Query/Condition.php).

#0 /var/www/html/web/core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php(27): Drupal\Core\Database\Query\Condition->condition('users_field_dat...', 'leakix, x', '=')
#1 /var/www/html/web/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php(80): Drupal\Core\Database\Query\Select->condition('users_field_dat...', Array, NULL)
#2 /var/www/html/web/core/lib/Drupal/Core/Entity/Query/Sql/Query.php(193): Drupal\Core\Entity\Query\Sql\Condition->compile(Object(Drupal\mysql\Driver\Database\mysql\Select))
#3 /var/www/html/web/core/lib/Drupal/Core/Entity/Query/Sql/Query.php(82): Drupal\Core\Entity\Query\Sql\Query->compile()
#4 /var/www/html/web/core/modules/user/user.module(299): Drupal\Core\Entity\Query\Sql\Query->execute()
#5 /var/www/html/web/core/modules/user/src/Controller/UserAuthenticationController.php(316): user_is_blocked(Array)
#6 /var/www/html/web/modules/contrib/login_emailusername/src/Controller/LoginEmailUsernameUserAuthenticationController.php(45): Drupal\user\Controller\UserAuthenticationController->userIsBlocked(Array)
#7 [internal function]: Drupal\login_emailusername\Controller\LoginEmailUsernameUserAuthenticationController->login(Object(Symfony\Component\HttpFoundation\Request))
#8 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array)
#9 /var/www/html/web/core/lib/Drupal/Core/Render/Renderer.php(637): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#10 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#11 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#12 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(181): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#13 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#14 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/Session.php(53): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#15 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#16 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/ContentLength.php(28): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#17 /var/www/html/web/core/modules/big_pipe/src/StackMiddleware/ContentLength.php(32): Drupal\Core\StackMiddleware\ContentLength->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#18 /var/www/html/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(116): Drupal\big_pipe\StackMiddleware\ContentLength->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#19 /var/www/html/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(90): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /var/www/html/web/core/modules/ban/src/BanMiddleware.php(50): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#21 /var/www/html/web/modules/contrib/crowdsec/src/Middleware.php(97): Drupal\ban\BanMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#22 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\crowdsec\Middleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#23 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#24 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/AjaxPageState.php(36): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#25 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\Core\StackMiddleware\AjaxPageState->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#26 /var/www/html/web/core/lib/Drupal/Core/DrupalKernel.php(741): Drupal\Core\StackMiddleware\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#27 /var/www/html/web/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#28 {main}

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Issue fork drupal-3591991

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

jurgenhaas created an issue. See original summary.

catch’s picture

Bumping this to major, I think we need to do more validation in that route before building the query - then throw a 400 if it's invalid (still an exception but the right one).

mukeshaddweb’s picture

Status: Active » Needs review
StatusFileSize
new1.44 KB

The login endpoint accepts non-string values for name/pass from the JSON decoder without validation, causing InvalidQueryException when an array reaches ->condition('name', $name) with an = operator.
is_string() added validation.

diff --git a/core/modules/user/src/Controller/UserAuthenticationController.php b/core/modules/user/src/Controller/UserAuthenticationController.php
index dfb55a9ee21..a883c65a7ca 100644
--- a/core/modules/user/src/Controller/UserAuthenticationController.php
+++ b/core/modules/user/src/Controller/UserAuthenticationController.php
@@ -185,6 +185,13 @@ public function login(Request $request) {
       throw new BadRequestHttpException('Missing credentials.pass.');
     }
 
+    if (!is_string($credentials['name'])) {
+      throw new BadRequestHttpException('Missing credentials.name.');
+    }
+    if (!is_string($credentials['pass'])) {
+      throw new BadRequestHttpException('Missing credentials.pass.');
+    }
+
     $this->floodControl($request, $credentials['name']);
 
     $account = FALSE;
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index fd105908d72..62f1b0d89a6 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -111,6 +111,9 @@ function user_validate_name($name) {
  */
 function user_is_blocked($name) {
   @trigger_error('user_is_blocked() is deprecated in drupal:11.0.0 and is removed from drupal:12.0.0. Use \Drupal\user\UserInterface::isBlocked() instead. See https://www.drupal.org/node/3411040', E_USER_DEPRECATED);
+  if (!is_string($name) || $name === '') {
+    return FALSE;
+  }
   return (bool) \Drupal::entityQuery('user')
     ->accessCheck(FALSE)
     ->condition('name', $name)
smustgrave’s picture

Status: Needs review » Needs work

Fixes need to be in MRs please, also bugs need test coverage

In this case the summary is also incomplete, please fill that in when setting to review

Thanks all