Изменить HTTP-ответы от расширения Chrome

83

Можно ли создать расширение Chrome, изменяющее тела ответов HTTP?

Я просмотрел API-интерфейсы расширений Chrome , но не нашел ничего для этого.

капитан дракон
источник
1
В общем нет. См. Https://code.google.com/p/chromium/issues/detail?id=104058 . Для подмножества вариантов использования - да. Не могли бы вы пояснить, почему вы хотите редактировать тела ответов?
Роб У
хорошо спасибо. напишите, пожалуйста, это как ответ, и я приму это.
капитан дракон
Если вы принимаете другие браузеры, тогда Firefox поддерживает webRequest.filterResponseData(). К сожалению, это решение только для Firefox.
Франклин Ю

Ответы:

49

Как правило, вы не можете изменить тело ответа HTTP-запроса с помощью стандартных API-интерфейсов расширения Chrome.

Эта функция запрашивается по адресу 104058: API WebRequest: разрешить расширению редактировать тело ответа . Пометьте проблему, чтобы получать уведомления об обновлениях.

Если вы хотите отредактировать тело ответа для известного XMLHttpRequest, введите код через сценарий содержимого, чтобы заменить XMLHttpRequestконструктор по умолчанию пользовательским (полнофункциональным), который перезаписывает ответ перед запуском реального события. Убедитесь, что ваш объект XMLHttpRequest полностью совместим со встроенным XMLHttpRequestобъектом Chrome , иначе сайты с AJAX-загрузкой сломаются.

В других случаях вы можете использовать API-интерфейсы chrome.webRequestили chrome.declarativeWebRequestдля перенаправления запроса на data:-URI. В отличие от XHR-подхода, вы не получите исходное содержимое запроса. Фактически, запрос никогда не попадет на сервер, потому что перенаправление может быть выполнено только до отправки фактического запроса. А если вы перенаправите main_frameзапрос, пользователь увидит data:-URI вместо запрошенного URL.

Роб В
источник
1
Я не думаю, что данные: -УРИ идея работает. Я просто пытался это сделать, и кажется, что CORS блокирует это. Страница, выполняющая исходный запрос, заканчивает тем, что говорит: «Запрос был перенаправлен на 'data: text / json;, {...}', что запрещено для запросов из разных источников, требующих предварительной проверки».
Джо
@Joe Невозможно воспроизвести в Chromium 39.0.2171.96
Rob W
1
@RobW, Какие есть какие-то хаки или решения, чтобы предотвратить изменение URL-адреса data:text...?
Pacerier
1
@Pacerier На самом деле нет удовлетворительного решения. В своем ответе я уже упоминал о возможности использования сценария содержимого для замены содержимого, но кроме этого вы не можете «изменить» ответ, не вызывая изменения URL-адреса.
Роб У
1
Я пробовал это решение. Обратите внимание, что вам может потребоваться переопределить fetch () помимо XMLHttpRequest. ограничение в том, что запросы браузера к js / images / css не перехватываются.
user861746
27

Я только что выпустил расширение Devtools, которое делает именно это :)

Это называется tamper, он основан на mitmproxy и позволяет вам видеть все запросы, сделанные текущей вкладкой, изменять их и обслуживать измененную версию при следующем обновлении.

Это довольно ранняя версия, но она должна быть совместима с OS X и Windows. Сообщите мне, если это не сработает для вас.

Вы можете получить его здесь http://dutzi.github.io/tamper/

Как это работает

Как прокомментировал @Xan ниже, расширение обменивается данными через собственный обмен сообщениями с помощью скрипта python, который расширяет mitmproxy .

В расширении перечислены все запросы, использующие chrome.devtools.network.onRequestFinished.

Когда вы щелкаете по запросу, он загружает свой ответ, используя метод объекта запроса getContent(), а затем отправляет этот ответ скрипту python, который сохраняет его локально.

Затем он открывает файл в редакторе (используется callдля OSX или subprocess.Popenдля Windows).

Сценарий python использует mitmproxy для прослушивания всех сообщений, передаваемых через этот прокси, если он обнаруживает запрос на файл, который был сохранен, он вместо этого обслуживает файл, который был сохранен.

Я использовал API прокси-сервера Chrome (в частности chrome.proxy.settings.set()), чтобы установить PAC в качестве настройки прокси. Этот файл PAC перенаправляет все сообщения на прокси-сервер сценария python.

Одна из величайших особенностей mitmproxy заключается в том, что он также может изменять связь по протоколу HTTP. Так что у вас тоже есть :)

дуци
источник
Интересное решение, хотя для этого требуется модуль Native Host.
Xan
5
Кстати, было бы полезно, если бы вы лучше объяснили используемую здесь технику.
Xan
10
интересное расширение Devtools! Однако кажется, что с помощью Tamper можно изменять только заголовки ответа, но не тело ответа .
Michael Trouw
3
Проблема с установкой.
Дмитрий Плешков
1
Можем ли мы изменить это тело ответа?
Kiran
17

Да. Это возможно с помощью chrome.debuggerAPI, который предоставляет расширению доступ к протоколу Chrome DevTools , который поддерживает перехват и изменение HTTP через свой сетевой API .

Это решение было предложено в комментарии к проблеме 487422 Chrome :

Для тех, кому нужна альтернатива, которая возможна на данный момент, вы можете использовать chrome.debuggerна странице фона / события, чтобы прикрепить к конкретной вкладке, которую вы хотите прослушать (или прикрепить ко всем вкладкам, если это возможно, не тестировали все вкладки лично) , затем используйте сетевой API протокола отладки.

Единственная проблема с этим заключается в том, что в верхней части области просмотра вкладки будет обычная желтая полоса, если пользователь не отключит ее chrome://flags.

Сначала прикрепите к цели отладчик:

chrome.debugger.getTargets((targets) => {
    let target = /* Find the target. */;
    let debuggee = { targetId: target.id };

    chrome.debugger.attach(debuggee, "1.2", () => {
        // TODO
    });
});

Далее отправляем Network.setRequestInterceptionEnabledкоманду, которая включит перехват сетевых запросов:

chrome.debugger.getTargets((targets) => {
    let target = /* Find the target. */;
    let debuggee = { targetId: target.id };

    chrome.debugger.attach(debuggee, "1.2", () => {
        chrome.debugger.sendCommand(debuggee, "Network.setRequestInterceptionEnabled", { enabled: true });
    });
});

Chrome начнет отправлять Network.requestInterceptedсобытия. Добавьте для них слушателя:

chrome.debugger.getTargets((targets) => {
    let target = /* Find the target. */;
    let debuggee = { targetId: target.id };

    chrome.debugger.attach(debuggee, "1.2", () => {
        chrome.debugger.sendCommand(debuggee, "Network.setRequestInterceptionEnabled", { enabled: true });
    });

    chrome.debugger.onEvent.addListener((source, method, params) => {
        if(source.targetId === target.id && method === "Network.requestIntercepted") {
            // TODO
        }
    });
});

В слушателе params.requestбудет соответствующий Requestобъект.

Отправьте ответ с помощью Network.continueInterceptedRequest:

  • Передайте кодировку base64 желаемого необработанного ответа HTTP ( включая строку состояния HTTP, заголовки и т. Д.! ) Как rawResponse.
  • Передайте params.interceptionIdкак interceptionId.

Обратите внимание, что я вообще ничего из этого не тестировал.

MultiplyByZer0
источник
Выглядит очень многообещающе, хотя я пробую это сейчас (Chrome 60), и либо мне что-то не хватает, либо это все еще невозможно; этот setRequestInterceptionEnabledметод, похоже, не включен в протокол DevTools v1.2, и я не могу найти способ прикрепить его к последней версии (вершина дерева).
Aioros
1
Я попробовал это решение, и оно в какой-то степени сработало. Если вы хотите изменить запрос, это решение подойдет. Если вы хотите изменить ответ на основе ответа, возвращенного сервером, нет никакого способа. в этот момент нет ответа. of coz вы можете перезаписать поле rawresponse, как сказал автор.
user861746
1
chrome.debugger.sendCommand(debuggee, "Network.setRequestInterceptionEnabled", { enabled: true });сбой с 'Network.setRequestInterceptionEnabled' не найден '
Pacerier
1
@ MultiplyByZer0, Ок, удалось заставить его работать. i.stack.imgur.com/n0Gff.png Однако необходимость удаления «верхней панели», т.е. необходимость установки флага браузера с последующим перезапуском браузера, означает, что необходимо альтернативное реальное решение.
Pacerier
@Pacerier. Вы правы, это не идеальное решение, но я не знаю лучшего. Кроме того, как вы заметили, этот ответ неполный и требует обновления. Надеюсь, я скоро это сделаю.
MultiplyByZer0
13

Как сказал @Rob w, я переопределил, XMLHttpRequestи это результат модификации любых запросов XHR на любых сайтах (работающих как прозрачный прокси-сервер модификации):

var _open = XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, URL) {
    var _onreadystatechange = this.onreadystatechange,
        _this = this;

    _this.onreadystatechange = function () {
        // catch only completed 'api/search/universal' requests
        if (_this.readyState === 4 && _this.status === 200 && ~URL.indexOf('api/search/universal')) {
            try {
                //////////////////////////////////////
                // THIS IS ACTIONS FOR YOUR REQUEST //
                //             EXAMPLE:             //
                //////////////////////////////////////
                var data = JSON.parse(_this.responseText); // {"fields": ["a","b"]}

                if (data.fields) {
                    data.fields.push('c','d');
                }

                // rewrite responseText
                Object.defineProperty(_this, 'responseText', {value: JSON.stringify(data)});
                /////////////// END //////////////////
            } catch (e) {}

            console.log('Caught! :)', method, URL/*, _this.responseText*/);
        }
        // call original callback
        if (_onreadystatechange) _onreadystatechange.apply(this, arguments);
    };

    // detect any onreadystatechange changing
    Object.defineProperty(this, "onreadystatechange", {
        get: function () {
            return _onreadystatechange;
        },
        set: function (value) {
            _onreadystatechange = value;
        }
    });

    return _open.apply(_this, arguments);
};

например, этот код может успешно использоваться Tampermonkey для внесения любых изменений на любых сайтах :)

MixerOID
источник
1
Я использовал ваш код, и он регистрировал ошибку на консоли, но не изменил ответ, который получило мое приложение (Angular).
Андре Рогжери Кампос
2
@ AndréRoggeriCampos Я столкнулся с тем же самым. Угловое использует более новый , responseа не responseText, так что все , что вам нужно сделать , это изменить Object.defineProperty использовать responseвместо
Джонатан Gawrych
Большое спасибо ! он работает с моим расширением Chrome!
Lancer.Yan 05