Chrome拡張機能からのHTTP応答を変更する


83

HTTP応答本文を変更するChrome拡張機能を作成することは可能ですか?

Chrome拡張APIを調べましたが、これを行うための方法が見つかりませんでした。


1
一般的にはありません。https://code.google.com/p/chromium/issues/detail?id=104058を参照してください。ユースケースのサブセットについては、はい。応答本文を編集する理由を明確にできますか?
ロブW

わかりました、ありがとう。答えとして書いてください、それを受け入れます。
キャプテンドラゴン

他のブラウザを受け入れる場合、FirefoxはをサポートしますwebRequest.filterResponseData()。残念ながら、これはFirefoxのみのソリューションです。
フランクリンユー

回答:


49

通常、標準のChrome拡張APIを使用してHTTPリクエストの応答本文を変更することはできません

この機能は104058でリクエストされています:WebRequest API:拡張機能が応答本文を編集できるようにします。問題にスターを付けて、更新の通知を受け取ります。

あなたは、編集に知られているためレスポンスボディたい場合はXMLHttpRequestコンテンツのスクリプトによるジェクトコードデフォルト上書きするXMLHttpRequest(フル機能)カスタム実際のイベントをトリガする前に応答を書き換えて1を持つコンストラクタを。XMLHttpRequestオブジェクトがChromeの組み込みオブジェクトに完全に準拠していることを確認してください。準拠していないとXMLHttpRequest、AJAXを多用するサイトが破損します。

それ以外の場合は、chrome.webRequestまたはchrome.declarativeWebRequestAPIを使用してリクエストをdata:-URIにリダイレクトできます。XHRアプローチとは異なり、リクエストの元のコンテンツは取得されません。実際には、リダイレクトは実際の要求が送信される前にしか実行できないため、要求がサーバーに到達することはありません。また、main_frameリクエストをリダイレクトすると、ユーザーdata:にはリクエストされたURLの代わりに-URIが表示されます。


1
私はデータがないと思います:-URIのアイデアは機能します。これを実行しようとしましたが、CORSがブロックしているようです。元のリクエストを作成したページは、「リクエストは 'data:text / json;、{...}'にリダイレクトされました。これは、プリフライトを必要とするクロスオリジンリクエストでは許可されていません。」
ジョー

@JoeはChromium39.0.2171.96で複製できません
Rob W

1
@ RobW、URLがに変わるのを防ぐためのハックや解決策は何data:text...ですか?
Pacerier 2017

1
@Pacerier本当に満足のいく解決策はありません。私の回答では、コンテンツスクリプトを使用してコンテンツを置き換えるオプションについてはすでに説明しましたが、それ以外の場合は、URLを変更せずに応答を「変更」することはできません。
ロブW

1
私はこの解決策を試しました。XMLHttpRequest以外にfetch()のオーバーライドが必要になる場合があることに注意してください。制限は、js / images / cssへのブラウザのリクエストが傍受されないことです。
user861746 2018年

27

私はちょうどそれを行うDevtools拡張機能をリリースしました:)

これはタンパーと呼ばれ、mitmproxyに基づいており、現在のタブからのすべてのリクエストを確認し、それらを変更して、次に更新するときに変更されたバージョンを提供できます。

これはかなり初期のバージョンですが、OSXおよびWindowsと互換性があるはずです。うまくいかない場合はお知らせください。

ここで入手できますhttp://dutzi.github.io/tamper/

これがどのように機能するか

@Xanが以下にコメントしているように、拡張機能はネイティブメッセージングを介してmitmproxyを拡張するPythonスクリプトと通信します。

拡張機能は、を使用してすべてのリクエストを一覧表示しますchrome.devtools.network.onRequestFinished

リクエストをクリックすると、リクエストオブジェクトのgetContent()メソッドを使用してレスポンスがダウンロードされ、そのレスポンスがPythonスクリプトに送信されてローカルに保存されます。

次に、エディターでファイルを開きます(callOSXまたはsubprocess.PopenWindows用を使用)。

Pythonスクリプトは、mitmproxyを使用して、そのプロキシを介して行われたすべての通信をリッスンします。保存されたファイルの要求を検出すると、代わりに保存されたファイルを提供します。

ChromeのプロキシAPI(具体的にはchrome.proxy.settings.set())を使用して、PACをプロキシ設定として設定しました。そのPACファイルは、すべての通信をPythonスクリプトのプロキシにリダイレクトします。

mitmproxyの最大の利点の1つは、HTTPS通信も変更できることです。だからあなたもそれを持っています:)


ネイティブホストモジュールが必要ですが、興味深いソリューションです。
xan 2014年

5
ちなみに、ここで使用されている手法をよりよく説明すると役立ちます。
xan 2014年

9
興味深いDevtools拡張機能!ただし、Tamperを使用して変更できるのは応答ヘッダーのみであり、応答本文は変更できないようです。
Michael Trouw 2017

3
インストールに関する問題。
Dmitry Pleshkov 2018年

1
これで応答本文を変更できますか?
キラン2018

17

はい。これは、ネットワークAPIを介したHTTPインターセプトと変更をサポートするChromeDevToolsプロトコルchrome.debuggerへの拡張アクセスを許可するAPIで可能です。

この解決策は、Chrome Issue487422へのコメントによって提案されまし

現時点で実行可能な代替手段が必要な場合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ます:

  • ご希望のHTTPレスポンス生のBase64エンコード渡し(HTTPステータスラインを含む、ヘッダなどを!)としてrawResponse
  • params.interceptionIdとして渡しますinterceptionId

私はこれをまったくテストしていないことに注意してください。


私は今それを試していますが(Chrome 60)、何かが足りないか、それでも不可能ですが、非常に有望に見えます。このsetRequestInterceptionEnabledメソッドはDevToolsプロトコルv1.2に含まれていないようで、代わりに最新の(ツリーのヒント)バージョンにアタッチする方法が見つかりません。
aioros 2017

1
私はこの解決策を試しましたが、ある程度は機能しました。リクエストを変更したい場合は、このソリューションが適しています。サーバーから返された応答に基づいて応答を変更する場合、方法はありません。その時点では応答はありません。cozの場合、作成者が言ったように、rawresponseフィールドを上書きできます。
user861746 2018年

1
chrome.debugger.sendCommand(debuggee, "Network.setRequestInterceptionEnabled", { enabled: true });「Network.setRequestInterceptionEnabled」で失敗したが」が見つかりませんでした
Pacerier

1
@ MultipleByZer0、OKはなんとかそれを機能させることができました。i.stack.imgur.com/n0Gff.png ただし、「トップバー」を削除する必要がある、つまりブラウザフラグを設定してからブラウザを再起動する必要があるということは、別の実際の解決策が必要であることを意味します。
ペースリエ

@Pacerierあなたはこの解決策が理想的ではないということは正しいですが、私はこれ以上の解決策を知りません。また、お気づきのとおり、この回答は不完全であり、更新する必要があります。うまくいけば、すぐにそれを行います。
MultiplyByZer0

12

@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が任意のサイトで変更を加えるために正常に使用できます:)


1
私はあなたのコードを使用しました、そしてそれはコンソールでキャッチを記録しました、しかしそれは私のアプリケーションが得た応答(Angular)を変えませんでした。
アンドレ・Roggeriカンポス

2
@AndréRoggeriCampos私は同じことに遭遇しました。Angularはではresponseなく新しいものresponseTextを使用するため、response代わりに使用するようにObject.definePropertyを変更するだけです
JonathanGawrych19年

どうもありがとうございました !それは私のChrome拡張機能で動作します!
Lancer.Yan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.