web-dev-qa-db-fra.com

Modifier les réponses HTTP à partir d'une extension Chrome

Est-il possible de créer une extension Chrome qui modifie le corps des réponses HTTP?)

J'ai consulté = Chrome Extension APIs , mais je n'ai rien trouvé pour cela.

61
captain dragon

En général, vous ne pouvez pas modifier le corps de la réponse d'une requête HTTP à l'aide des API d'extension standard Chrome.

Cette fonctionnalité est demandée à l'adresse 104058: API WebRequest: autoriser l'extension à modifier le corps de la réponse . Lancez le numéro pour être averti des mises à jour.

Si vous souhaitez modifier le corps de la réponse pour un XMLHttpRequest, injecter du code via un script de contenu pour remplacer le constructeur par défaut XMLHttpRequest par un script personnalisé (complet). en vedette) qui réécrit la réponse avant de déclencher l'événement réel. Assurez-vous que votre objet XMLHttpRequest est entièrement compatible avec l'objet intégré XMLHttpRequest de Chrome, sinon les sites AJAX lourds vont tomber en panne.

Dans d'autres cas, vous pouvez utiliser le chrome.webRequest ou chrome.declarativeWebRequest API pour rediriger la demande vers un data: _ URI. Contrairement à l'approche XHR, vous n'obtiendrez pas le contenu original de la demande. En réalité, la demande ne parviendra jamais sur le serveur car la redirection ne peut être effectuée qu'avant l'envoi de la demande. Et si vous redirigez un main_frame demande, l’utilisateur verra le data: _ URI au lieu de l'URL demandée.

38
Rob W

Je viens de publier une extension Devtools qui ne fait que ça :)

Elle s'appelle tamper, elle est basée sur mitmproxy et vous permet de voir toutes les demandes faites par l'onglet actuel, de les modifier et de servir la version modifiée à la prochaine actualisation.

C'est une version assez ancienne mais elle devrait être compatible avec OS X et Windows. Faites-moi savoir si cela ne fonctionne pas pour vous.

Vous pouvez l'obtenir ici http://dutzi.github.io/tamper/

Comment ça marche

Comme @Xan a commenté ci-dessous, l'extension communique par le biais de Native Messaging avec un script python qui étend mitmproxy .

L'extension liste toutes les requêtes utilisant chrome.devtools.network.onRequestFinished.

Lorsque vous cliquez sur l'une des requêtes, il télécharge sa réponse à l'aide de la méthode getContent() de l'objet de requête, puis envoie cette réponse au script python qui l'enregistre localement).

Il ouvre ensuite le fichier dans un éditeur (en utilisant call pour OSX ou subprocess.Popen Pour Windows).

Le script python utilise mitmproxy pour écouter toutes les communications établies via ce proxy. S'il détecte une demande de fichier enregistré, il sert le fichier qui a été enregistré à la place.

J'ai utilisé l'API proxy de Chrome (en particulier chrome.proxy.settings.set()) pour définir un PAC en tant que paramètre de proxy. Ce fichier PAC redirige toutes les communications vers le proxy du script python.

L'un des principaux avantages de mitmproxy est qu'il peut également modifier la communication HTTP. Donc vous avez ça aussi :)

21
dutzi

Oui. C'est possible avec le chrome.debugger API, qui accorde un accès d'extension à protocole Chrome DevTools , qui prend en charge l'interception et la modification HTTP via son API résea .

Cette solution a été suggérée par n commentaire sur Chrome Issue 487422 :

Pour ceux qui souhaitent une alternative réalisable pour le moment, vous pouvez utiliser chrome.debugger dans une page d’arrière-plan/événement à attacher à l’onglet spécifique que vous voulez écouter (ou attacher à tous les onglets si cela est possible, n’a pas testé tous les onglets personnellement), puis utilisez l’API réseau du protocole de débogage.

Le seul problème avec cela est qu'il y aura la barre jaune habituelle en haut de la fenêtre de l'onglet, à moins que l'utilisateur ne le désactive dans chrome://flags.

Tout d’abord, attachez un débogueur à la cible:

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

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

Ensuite, envoyez le Network.setRequestInterceptionEnabled commande, qui permettra l’interception des requêtes réseau:

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 va maintenant commencer à envoyer Network.requestIntercepted événements. Ajoutez un auditeur pour eux:

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
        }
    });
});

Dans l'auditeur, params.request sera l’objet Request correspondant.

Envoyez la réponse avec Network.continueInterceptedRequest :

  • Passez un encodage en base64 de votre réponse brute HTTP souhaitée (, y compris la ligne d’état HTTP, les en-têtes, etc.! ) sous la forme rawResponse.
  • Passer params.interceptionId comme interceptionId.

Notez que je n'ai pas du tout testé cela.

9
MultiplyByZer0

Comme @Rob w, j'ai remplacé XMLHttpRequest et il en résulte une modification des requêtes XHR de tous les sites (fonctionnant comme un proxy de modification transparent):

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);
};

par exemple, Tampermonkey peut utiliser ce code avec succès pour apporter des modifications sur n’importe quel site :)

8
MixerOID