在内容脚本之前注入javascript变量

在内容脚本之前注入javascript变量,javascript,google-chrome-extension,Javascript,Google Chrome Extension,使用我的后台脚本background.js,我需要先将一个动态变量作为内容脚本注入,然后再将另一个文件inject.js也作为内容脚本注入。在运行页面上的任何脚本之前,Inject.js需要访问该变量并运行其代码。从inject.js内容脚本访问动态变量时遇到困难 manifest.json { "name": "Shape Shifter", "version": "1.0", "description": "Anti browser fingerprinting web extension.

使用我的后台脚本background.js,我需要先将一个动态变量作为内容脚本注入,然后再将另一个文件inject.js也作为内容脚本注入。在运行页面上的任何脚本之前,Inject.js需要访问该变量并运行其代码。从inject.js内容脚本访问动态变量时遇到困难

manifest.json

{
"name": "Shape Shifter",
"version": "1.0",
"description": "Anti browser fingerprinting web extension. Generates randomised values for HTTP request headers, javascript property values and javascript method return types.",
"manifest_version": 2,
"icons": {
    "32": "icons/person-32.png",
    "48": "icons/person-48.png"
},
"background": {
    "persistent": true,
    "scripts": ["js/ua.js", "js/words.js", "js/lib/seedrandom.min.js", "js/random.js", "js/background.js"]
},
"browser_action": {
    "default_title": "Shape Shifter",
    "default_icon": "icons/person-32.png",
    "default_popup": "html/popup.html"
},
"content_scripts": [
  {
    "run_at": "document_end",
    "matches": ["<all_urls>"],
    "js": ["js/inject.js"] 
  }
],
"permissions": [
    "webRequest",
    "webRequestBlocking",
    "webNavigation",
    "tabs",
    "activeTab",
    "storage",
    "<all_urls>"
],
"web_accessible_resources": [
    "js/ua.js",
    "js/words.js",
    "js/lib/seedrandom.min.js",
    "js/random.js",
    "js/api/document.js",
    "js/api/navigator.js",
    "js/api/canvas.js",
    "js/api/history.js",
    "js/api/battery.js",
    "js/api/audio.js",
    "js/api/element.js"
]
尝试将种子记录到控制台时出现错误:

inject.js:26 Uncaught ReferenceError: seed is not defined
    at inject.js:26
    at inject.js:52
有什么想法吗?

这会很棘手。 让我们看看您的需求

在运行页面上的任何脚本之前,Inject.js需要访问该变量并运行其代码

这不是您的代码目前的工作方式。您的
inject.js
document\u end
执行-这发生在整个DOM树解析之后,这意味着在所有页面脚本运行之后(禁止异步部分和
async
脚本加载)

Chrome有一个解决方案-您可以将执行设置为
document\u start
。然后,您的代码将真正先于其他所有内容运行,而DOM仍然没有被解析(因此
document
基本上是空的)。对于代码所做的,它不应该产生问题(它只依赖于将存在的
document.documentElement

问题是,您的所有代码都必须是同步的,才能仍然享受“先运行再运行”属性。只要代码的同步部分运行,Chrome就会暂停DOM解析,但是当它愉快地继续解析(并从中运行代码)文档时,所有的赌注都被取消了

例如,这就取消了
chrome.storage
和消息传递的资格,因为访问chrome.storage必须是异步的

我需要[在页面加载时]注入一个动态变量

这意味着您无法将其提前存储在某些同步可用的存储中(例如,在
本地存储中或网站的cookie中),考虑到您事先不知道域,这将是一个问题

请注意,特别是对于您的代码,这可能不是一个很大的因素;实际上,每个域的“动态”值是固定的。您仍然不知道将访问哪个域,但您至少可以保证在第二次加载时它会在那里

使用我的后台脚本background.js,在注入另一个文件之前,我需要注入一个动态变量作为内容脚本[该文件仍然需要在页面上所有其他文件之前运行]

这是棘手的部分事实上,如前所述,这根本不可能。您正试图从后台捕捉导航提交、Chrome将页面切换到新域和执行
文档开始
脚本之间的确切时刻

那里没有可检测的间隙,也没有办法告诉Chrome等待。这是一个你没有希望解决的比赛状态

您正在尝试使用
webNavigation.onBeforeNavigate
——甚至在提交导航之前。因此,您的
injectScript
甚至可能会转到上一页,使其变得无用。如果你尝试其他活动,例如
onCommitted
,仍然无法确定何时会处理
injectScript
。可能在你的剧本之后

那么,如何解决这些问题呢? 幸运的是,有一些同步存储可用于内容脚本,您可以在最早的脚本执行之前将一些信息推送到内容脚本

饼干。

但是,使用
chrome.cookies
API不会有帮助。您需要在
webRequest.onHeadersReceived
上主动将cookie值注入请求

您必须准备好同步的值,以便使用阻塞处理程序将其处理到
onHeadersReceived
,但是您只需添加一个
Set Cookie
头,并立即在
document.cookies
中的
inject.js
中使用它

  • background.js

    "use strict";
    
    console.log("Background Script Running ...");
    
    function getSeed(origin) {
        // Get a Storage object
        var storage = window.localStorage;
    
        // Do we already have a seed in storage for this origin or not?
        var seed = storage.getItem(origin);
    
        if (seed === null) {
            // Initialise a 32 byte buffer
            seed = new Uint8Array(32);
    
            // Fill it with cryptographically random values
            window.crypto.getRandomValues(seed);
    
            // Save it to storage
            storage.setItem(origin, seed);
        }
    
        return seed;
    }
    
    // Methods to get HTTP headers
    function getAcceptHeader(seed) {
        return "NotYetImplemented";
    }
    function getAcceptCharsetHeader(seed) {
        return "NotYetImplemented";
    }
    function getAcceptEncodingHeader(seed) {
        return "NotYetImplemented";
    }
    function getAcceptLanguageHeader() {
        // NOTE: TOR Browser uses American English
        return "en-US,en;q=0.5";
    }
    function getAuthorizationHeader(seed) {
        return "NotYetImplemented";
    }
    function getExpectHeader(seed) {
        return "NotYetImplemented";
    }
    function getFromHeader(seed) {
        return "NotYetImplemented";
    }
    function getHostHeader(seed) {
        return "NotYetImplemented";
    }
    function getIfMatchHeader(seed) {
        return "NotYetImplemented";
    }
    function getIfModifiedSinceHeader(seed) {
        return "NotYetImplemented";
    }
    function getIfNoneMatchHeader(seed) {
        return "NotYetImplemented";
    }
    function getIfRangeHeader(seed) {
        return "NotYetImplemented";
    }
    function getIfUnmodifiedSinceHeader(seed) {
        return "NotYetImplemented";
    }
    function getMaxForwardsHeader(seed) {
        return "NotYetImplemented";
    }
    function getProxyAuthorizationHeader(seed) {
        return "NotYetImplemented";
    }
    function getRangeHeader(seed) {
        return "NotYetImplemented";
    }
    function getRefererHeader() {
        // NOTE: From https://developer.mozilla.org/en-US/docs/Web/API/Document/referrer
        // NOTE: The value is an empty string if the user navigated to the page directly (not through a link, but, for example, via a bookmark).
        // NOTE: Since this property returns only a string, it does not give you DOM access to the referring page.
    
        // NOTE: Make websites think we always go to them directly rather than being referred.
        return "";
    }
    function getTEHeader(seed) {
        return "NotYetImplemented";
    }
    function getUserAgentHeader(seed) {
        Math.seedrandom(seed);
    
        return userAgents[randomNumber(0, userAgents.length)];
    }
    
    function rewriteHttpHeaders(e) {
        // Create URL object from url string
        var serverUrl = new URL(e.url);
    
        console.log(e);
    
        // Get the origin (hostname)
        var origin = serverUrl.hostname;
    
        var seed = getSeed(origin);
    
        console.log("Background - Seed for origin " + origin + ": " + seed);
    
        for (var header of e.requestHeaders) {
            if (header.name.toLowerCase() === "accept") {
            }
            else if (header.name.toLowerCase() === "accept-charset") {
            }
            else if (header.name.toLowerCase() === "accept-encoding") {
            }
            else if (header.name.toLowerCase() === "accept-language") {
                header.value = getAcceptLanguageHeader();
            }
            else if (header.name.toLowerCase() === "authorization") {
            }
            else if (header.name.toLowerCase() === "expect") {
            }
            else if (header.name.toLowerCase() === "from") {
            }
            else if (header.name.toLowerCase() === "host") {
            }
            else if (header.name.toLowerCase() === "if-match") {
            }
            else if (header.name.toLowerCase() === "if-modified-since") {
            }
            else if (header.name.toLowerCase() === "if-none-match") {
            }
            else if (header.name.toLowerCase() === "if-range") {
            }
            else if (header.name.toLowerCase() === "if-unmodified-since") {
            }
            else if (header.name.toLowerCase() === "max-forwards") {
            }
            else if (header.name.toLowerCase() === "proxy-authorization") {
            }
            else if (header.name.toLowerCase() === "range") {
            }
            else if (header.name.toLowerCase() === "referer") {
                header.value = getRefererHeader();
            }
            else if (header.name.toLowerCase() === "te") {
            }
            else if (header.name.toLowerCase() === "user-agent") {
                header.value = getUserAgentHeader(seed);
            }
        }
    
        return {requestHeaders: e.requestHeaders};
    }
    
    chrome.webRequest.onBeforeSendHeaders.addListener(rewriteHttpHeaders, {urls: ["<all_urls>"]}, ["blocking", "requestHeaders"]);
    
    chrome.webNavigation.onBeforeNavigate.addListener(function(details) {
        // Create URL object from url string
        var serverUrl = new URL(details.url);
    
        // Get the origin (hostname)
        var origin = serverUrl.hostname;
    
        var seed = "Some dynamic value";
    
        console.log("Injecting Value");
        chrome.tabs.executeScript(details.tabId, {code: "var seed = '" + seed + "';console.log(seed);", runAt: "document_start"}, function() {
            console.log("Value Injected");
        });
    });
    
    函数addSeedCookie(详细信息){
    seed=SomethingSynchronous();
    详细信息。负责人。推送({
    名称:“设置Cookie”,
    值:`seed\u goes\u here=${seed}`
    });
    返回{
    负责人:详细信息。负责人
    };
    }
    chrome.webRequest.onHeadersReceived.addListener(
    addSeedCookie,{URL:[“”]}[
    “封锁”,
    “负责人”,
    //Chrome 72+需要“extraHeaders”来处理设置Cookie头
    chrome.webRequest.OnHeadersReceivedOptions.EXTRA_头,
    ].filter(布尔值)
    );
    
  • inject.js

    (function() {
      function inject(filePath) {
        var script = document.createElement('script');
        script.src = chrome.extension.getURL(filePath);
        script.onload = function() {
          this.remove();
        };
        (document.head || document.documentElement).appendChild(script);
      }
    
      function injectText(text) {
        var script = document.createElement('script');
        script.textContent = text;
        script.onload = function() {
          this.remove();
        };
        (document.head || document.documentElement).appendChild(script);
      }
    
      console.log("CONTENT SCRIPT RUNNING");
    
      console.log(seed); // SEED IS NOT DEFINED HERE ???
    
      injectText("var seed = 'hello';");
      console.log("[INFO] Injected Seed ...");
      inject("js/ua.js");
      console.log("[INFO] Injected UA ...");
      inject("js/words.js");
      console.log("[INFO] Injected Words ...");
      inject("js/lib/seedrandom.min.js");
      console.log("[INFO] Injected Seed Random ...");
      inject("js/random.js");
      console.log("[INFO] Injected Random ...");
      inject("js/api/document.js");
      console.log("[INFO] Injected Document API ...");
      inject("js/api/navigator.js");
      console.log("[INFO] Injected Navigator API ...");
      inject("js/api/canvas.js");
      console.log("[INFO] Injected Canvas API ...");
      inject("js/api/history.js");
      console.log("[INFO] Injected History API ...");
      inject("js/api/battery.js");
      console.log("[INFO] Injected Battery API ...");
      inject("js/api/audio.js");
      console.log("[INFO] Injected Audio API ...");
      inject("js/api/element.js");
      console.log("[INFO] Injected Element API ...");
    })();
    
    函数getCookie(cookie){//https://stackoverflow.com/a/19971550/934239
    return document.cookie.split(“;”).reduce(函数(prev,c){
    var arr=c.split('=');
    返回(arr[0].trim()==cookie)?arr[1]:prev;
    },未定义);
    }
    var seed=getCookie(“seed_到这里去”);
    
如果需要异步函数生成数据,请在onBeforeRequest事件中发送请求之前准备数据,然后在onHeadersReceived侦听器中使用它

constpreparedseed={};
chrome.webRequest.onBeforeRequest.addListener(
详细信息=>{
chrome.storage.local.get('seed',data=>{
preparedSeed[details.requestId]=data.seed;
});
}, {
网址:[''],
类型:[“主框架”、“子框架”],
});

注意:以上代码未经测试,仅用于说明想法。

当您登录到该行时,您希望在哪里定义
seed
?相关:看起来这是一个棘手的问题。我最终找到了一个完全不同的解决方案。谢谢你的帮助。哇,你是怎么想出这个Xan的?天才。