AJAX Response Not Sanitized For Length

Created on 16 December 2024, 6 days ago

Background Information

Some important background information to understand the problem:

  • Drupal now sets a content-length header for most responses ( https://www.drupal.org/node/3298551 β†’ ). The length is set in web/core/lib/Drupal/Core/StackMiddleware/ContentLength.php. The length is
    strlen($content) 

    .

  • NGINX strictly follows the content-length header. If content-length=5 and it receives 6 characters, it truncates the last one.
  • It is possible to send data outside of the AJAX response. For example, whitespace before the open PHP tag is sent.

Problem/Motivation

We noticed that some AJAX calls were failing on a Drupal site. The browser was getting a response. But, the last two characters of the response were truncated. This caused an invalid response so the AJAX call failed.

We noticed that there were two characters of whitespace added to the beginning of AJAX responses. For example, Drupal built a response like:
[{"command":"update_build_id","old":"form-rNk1NjBKUEWXrIPXv8_MQPNQEtl3YeI9JxUbxMo5TnU","new":"form-SPxinqYLQeRC0LYR5C6Pb7hgUQYtWcLqqSl-eyXEp64"},{"command":"insert","method":"html","selector":"[data-virgil-converse-widget-search-response]","data":"You might find the following items helpful in your situation","settings":null}]

But the browser received a response like:
[{"command":"update_build_id","old":"form-rNk1NjBKUEWXrIPXv8_MQPNQEtl3YeI9JxUbxMo5TnU","new":"form-Sunh74tKhjS_6AmYNsYgr1R__itU3kZ3NSHZTV8qpK0"},{"command":"insert","method":"html","selector":"[data-virgil-converse-widget-search-response]","data":"You might find the following items helpful in your situation","settings":null
Note the two whitespace at the beginning and the missing characters at the end. We back-tracked this to whitespace introduced before the open PHP tags on a custom .theme file.

Drupal adds the content-length header. It would be nice if Drupal did more to make sure the response is actually the length Drupal is setting.

Steps to reproduce

  1. Set up a Drupal site
  2. Use a default admin theme. Let's say Claro.
  3. Edit a view in the Views admin UI. Click any link that makes an AJAX call. For example, change the title. Confirm that it works.
  4. Edit web/core/themes/claro/claro.theme. Add whitespace before the open PHP tag (" <?php")
  5. Retest the link in the Views admin UI. It should fail. You won't see any change on screen.
  6. Use the browser developer tools, network tab. The request will likely have status 200. But, if you dig into the response, you will see a syntax error. If you read the response, it will be missing characters at the end.

Proposed resolution

Protect AJAX responses so only the content in the response is sent. For example, I added AjaxResponse->send() to override Response->send(). In AjaxResponse->send(), I:

  1. ob_clean() to dump anything in the output buffer (like whitespace that happened to be before the open PHP tag)
  2. execute the parent send() method

For reference:

  • web/core/lib/Drupal/Core/Ajax/AjaxResponse.php extends vendor/symfony/http-foundation/JsonResponse.php
  • JsonResponse extends vendor/symfony/http-foundation/Response.php

Remaining tasks

I don't know the implications of doing a ob_clean() before sending the response. Could someone with more expertise please weigh in on any unintended consequences?

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

πŸ› Bug report
Status

Active

Version

10.3 ✨

Component

ajax system

Created by

πŸ‡ΊπŸ‡ΈUnited States JasonSafro

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Production build 0.71.5 2024