Czy można utworzyć rozszerzenie do Chrome, które modyfikuje treść odpowiedzi HTTP?
Szukałem w Chrome Extension API , ale nie znalazłem nic, aby to zrobić.
Czy można utworzyć rozszerzenie do Chrome, które modyfikuje treść odpowiedzi HTTP?
Szukałem w Chrome Extension API , ale nie znalazłem nic, aby to zrobić.
webRequest.filterResponseData()
. Niestety jest to rozwiązanie tylko dla przeglądarki Firefox.
Odpowiedzi:
Ogólnie rzecz biorąc, nie można zmienić treści odpowiedzi żądania HTTP przy użyciu standardowych interfejsów API rozszerzeń Chrome.
Ta funkcja jest wymagana pod adresem 104058: WebRequest API: zezwól rozszerzeniu na edytowanie treści odpowiedzi . Oznacz problem gwiazdką, aby otrzymywać powiadomienia o aktualizacjach.
Jeśli chcesz edytować treść odpowiedzi dla znanego XMLHttpRequest
, wstrzykuj kod za pomocą skryptu zawartości, aby zastąpić domyślny XMLHttpRequest
konstruktor niestandardowym (w pełni funkcjonalnym), który przepisuje odpowiedź przed wyzwoleniem rzeczywistego zdarzenia. Upewnij się, że Twój obiekt XMLHttpRequest jest w pełni zgodny z wbudowanym XMLHttpRequest
obiektem Chrome, w przeciwnym razie strony z dużą ilością technologii AJAX ulegną awarii.
W innych przypadkach możesz użyć interfejsów API chrome.webRequest
lub, chrome.declarativeWebRequest
aby przekierować żądanie do data:
-URI. W przeciwieństwie do podejścia XHR, nie otrzymasz oryginalnej treści żądania. W rzeczywistości żądanie nigdy nie dotrze do serwera, ponieważ przekierowanie można wykonać tylko przed wysłaniem rzeczywistego żądania. Jeśli przekierowujesz main_frame
żądanie, użytkownik zobaczy data:
-URI zamiast żądanego adresu URL.
data:text...
?
Właśnie wydałem rozszerzenie Devtools, które właśnie to robi :)
Nazywa się tamper, jest oparty na mitmproxy i pozwala zobaczyć wszystkie żądania wysyłane przez bieżącą kartę, modyfikować je i obsługiwać zmodyfikowaną wersję przy następnym odświeżeniu.
To dość wczesna wersja, ale powinna być kompatybilna z OS X i Windows. Daj mi znać, jeśli to nie zadziała.
Możesz go pobrać tutaj http://dutzi.github.io/tamper/
Jak to działa
Jak skomentował @Xan poniżej, rozszerzenie komunikuje się poprzez Native Messaging ze skryptem Pythona, który rozszerza mitmproxy .
Rozszerzenie wyświetla listę wszystkich żądań używających chrome.devtools.network.onRequestFinished
.
Po kliknięciu żądań pobiera swoją odpowiedź przy użyciu metody obiektu żądania getContent()
, a następnie wysyła tę odpowiedź do skryptu Pythona, który zapisuje ją lokalnie.
Następnie otwiera plik w edytorze (używając call
dla OSX lub subprocess.Popen
dla Windows).
Skrypt w Pythonie używa mitmproxy do nasłuchiwania całej komunikacji prowadzonej przez to proxy, jeśli wykryje żądanie dotyczące zapisanego pliku, zamiast tego obsługuje zapisany plik.
Użyłem API proxy Chrome (w szczególności chrome.proxy.settings.set()
), aby ustawić PAC jako ustawienie proxy. Ten plik PAC przekierowuje całą komunikację do serwera proxy skryptu Python.
Jedną z największych zalet mitmproxy jest to, że może również modyfikować komunikację HTTPs. Więc to też masz :)
Tak. Jest to możliwe dzięki chrome.debugger
interfejsowi API, który zapewnia rozszerzeniu dostęp do protokołu Chrome DevTools , który obsługuje przechwytywanie i modyfikację HTTP za pośrednictwem sieciowego interfejsu API .
To rozwiązanie zostało zasugerowane w komentarzu do Chrome Issue 487422 :
Dla każdego, kto chce alternatywy, która jest obecnie możliwa, możesz użyć na stronie
chrome.debugger
w tle / wydarzenia, aby dołączyć do określonej karty, której chcesz słuchać (lub dołączyć do wszystkich kart, jeśli to możliwe, nie przetestowałem osobiście wszystkich kart) , a następnie użyj sieciowego API protokołu debugowania.Jedynym problemem jest to, że u góry widoku karty będzie widoczny zwykły żółty pasek, chyba że użytkownik wyłączy go w
chrome://flags
.
Najpierw dołącz debugger do celu:
chrome.debugger.getTargets((targets) => {
let target = /* Find the target. */;
let debuggee = { targetId: target.id };
chrome.debugger.attach(debuggee, "1.2", () => {
// TODO
});
});
Następnie wyślij Network.setRequestInterceptionEnabled
polecenie, które umożliwi przechwytywanie żądań sieciowych:
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 zacznie teraz wysyłać Network.requestIntercepted
wydarzenia. Dodaj dla nich słuchacza:
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
}
});
});
W odbiorniku params.request
będzie odpowiedni Request
obiekt.
Wyślij odpowiedź z Network.continueInterceptedRequest
:
rawResponse
.params.interceptionId
jako interceptionId
.Zwróć uwagę, że w ogóle tego nie testowałem.
setRequestInterceptionEnabled
metoda wydaje się nie być zawarte w protokole DevTools v1.2, a ja nie mogę znaleźć sposób, aby dołączyć go do najnowszej (tip-of-tree) zamiast wersji.
chrome.debugger.sendCommand(debuggee, "Network.setRequestInterceptionEnabled", { enabled: true });
nie powiodło się, a „Network.setRequestInterceptionEnabled” nie został znaleziony ”
Jak powiedział @Rob w, nadpisałem XMLHttpRequest
i jest to wynikiem modyfikacji wszelkich żądań XHR w dowolnych witrynach (działa jak przezroczysty proxy modyfikacji):
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);
};
na przykład ten kod może być z powodzeniem używany przez Tampermonkey do wprowadzania jakichkolwiek modyfikacji na dowolnych stronach :)
response
raczej nowszego niż responseText
, więc wszystko, co musisz zrobić, to zmienić Object.defineProperty, aby użyć response
zamiast tego