Почему Google вставляет while(1); в JSON и как не попасться на удочку: топ ошибок JavaScript-разработчиков
Если вы когда-нибудь заглядывали в консоль разработчика при работе с Google API или просто гуглили странные ответы от сервера, то наверняка замечали нечто вроде:
while(1);["some","data"]
Выглядит как бесконечный цикл, который повесит браузер, правда? Но на самом деле это гениальный защитный механизм. Давайте разберемся, зачем Google prepend while(1) к JSON, какие ошибки совершают разработчики при работе с такими ответами и как их избежать.
Этот вопрос — один из самых популярных на Stack Overflow (тысячи просмотров), и не зря. Он затрагивает не только технические тонкости, но и фундаментальные принципы веб-безопасности.
Что такое while(1); в JSON и откуда он взялся?
Начнем с главного. Google prepend while(1) к JSON не для того, чтобы сломать ваш код, а для защиты от атаки, которая называется JSON Hijacking (угон JSON).
Представьте старые добрые времена, когда сайты использовали массивы JSON верхнего уровня (например, [{"user":"admin","token":"secret"}]). Злоумышленник мог переопределить глобальный конструктор Array или Object и украсть данные через <script> тег. Браузер выполнял JSON как JavaScript, и данные утекали.
Решение Google — добавить префикс while(1);. Это делает JSON невалидным JavaScript (бесконечный цикл не даст выполниться вредоносному скрипту), но при этом сам JSON остается корректным — просто его нужно правильно распарсить, удалив префикс.
Сейчас эта практика стала стандартом безопасности для многих крупных API. Но если вы не знаете о ней, ваш код может сломаться.
Топ-5 ошибок JavaScript-разработчиков при работе с защищенным JSON
Ошибка №1: Попытка распарсить ответ как есть
Самая частая ошибка — разработчик получает ответ от сервера и сразу передает его в JSON.parse(). Результат — ошибка парсинга, потому что while(1); не является валидным JSON.
fetch('https://api.google.com/some-endpoint')
.then(response => response.text())
.then(data => {
// Ошибка! JSON.parse упадет
const parsed = JSON.parse(data);
console.log(parsed);
})
.catch(err => console.error('Ошибка парсинга:', err));
Как избежать: Всегда удаляйте префикс while(1); перед парсингом.
fetch('https://api.google.com/some-endpoint')
.then(response => response.text())
.then(data => {
// Удаляем защитный префикс
const cleanJson = data.replace(/^\)?\s*while\s*\(\s*1\s*\)\s*;\s*/, '');
const parsed = JSON.parse(cleanJson);
console.log(parsed);
})
.catch(err => console.error('Ошибка парсинга:', err));
Ошибка №2: Использование response.json() без обработки
Метод response.json() в Fetch API ожидает чистый JSON. Если сервер возвращает while(1);..., то response.json() выбросит исключение. Разработчики часто думают, что это баг API, а не особенность безопасности.
// Этот код упадет с ошибкой "Unexpected token w"
fetch('https://api.google.com/endpoint')
.then(response => response.json()) // Ошибка!
.then(data => console.log(data));
Как избежать: Используйте response.text() и затем вручную удаляйте префикс.
fetch('https://api.google.com/endpoint')
.then(response => response.text())
.then(text => {
const jsonText = text.replace(/^\)?\s*while\s*\(\s*1\s*\)\s*;\s*/, '');
return JSON.parse(jsonText);
})
.then(data => console.log(data));
Ошибка №3: Игнорирование других возможных префиксов
Google не единственный, кто использует защитные префиксы. Facebook использует for(;;);, другие сервисы — )]}'. Если вы пишете универсальный парсер, нужно учитывать разные варианты.
Как избежать: Сделайте универсальную функцию для очистки ответа.
function cleanJsonResponse(rawText) {
// Убираем популярные защитные префиксы
return rawText.replace(/^(\)?\s*while\s*\(\s*1\s*\)\s*;|for\s*\(\s*;\s*;\s*\)\s*;|\)\]\}'\s*)/, '');
}
fetch('https://api.example.com/data')
.then(res => res.text())
.then(text => {
const data = JSON.parse(cleanJsonResponse(text));
console.log(data);
});
Ошибка №4: Доверие к заголовкам Content-Type
Некоторые разработчики полагаются на заголовок Content-Type: application/json и думают, что ответ гарантированно является чистым JSON. Но защитные префиксы не меняют MIME-тип. Сервер может вернуть application/json с while(1); внутри.
Как избежать: Никогда не доверяйте заголовкам на 100%. Всегда проверяйте структуру ответа.
fetch(url)
.then(response => {
// Проверяем, но не полагаемся только на заголовок
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Не JSON ответ');
}
return response.text();
})
.then(text => {
// Все равно чистим
const data = JSON.parse(cleanJsonResponse(text));
// Дальнейшая обработка
});
Ошибка №5: Использование eval() для парсинга
В древних проектах можно встретить eval('(' + response + ')') вместо JSON.parse(). Если вы попытаетесь так обработать ответ с while(1);, то браузер зависнет навсегда. Это не просто ошибка — это катастрофа.
// НИКОГДА так не делайте!
const data = eval('(' + response + ')'); // Бесконечный цикл!
Как избежать: Забудьте про eval(). Используйте только JSON.parse() после очистки префикса.
Как Google защищает свои API: глубже, чем кажется
Помимо while(1);, Google использует дополнительные меры безопасности:
- Same-Origin Policy — стандартная защита браузера от кросс-доменных запросов.
- CORS — контроль доступа