在内容脚本之前注入JavaScript变量 [英] Injecting javascript variable before content script

查看:116
本文介绍了在内容脚本之前注入JavaScript变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用我的后台脚本background.js,我需要在注入另一个文件inject.js作为内容脚本之前注入一个动态变量作为内容脚本。 Inject.js将需要访问此变量并在页面上的任何脚本运行之前运行它的代码。我无法从inject.js内容脚本访问动态变量。



manifest.json

  {
name:Shape Shifter,
version:1.0,
description:反浏览器指纹识别Web扩展。 ,
manifest_version:2,
图标:{
32:icons / person- 32.png,
48:icons / person-48.png
},
background:{
persistent:true,
脚本:[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:图标/ person-32.png,
default_popup:html / popup.html
},
content_scripts:[
{
run_at:document_end,
:[< all_urls>],
js:[js / inject.js]
$ b权限:[
webRequest,
webRequestBlocking,
webNavigation,
tabs,
activeTab,
存储,
< 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
]

}



background.js

 use strict; 

console.log(后台脚本正在运行...);

函数getSeed(origin){
//获取一个存储对象
var storage = window.localStorage;

//我们是否已经有这种起源的存储种子?
var seed = storage.getItem(origin);

if(seed === null){
//初始化一个32字节的缓冲区
seed = new Uint8Array(32);

//用加密随机值填充
window.crypto.getRandomValues(seed);

//将其保存到存储区
storage.setItem(origin,seed);
}

返回种子;
}

//获取HTTP头文件的方法
函数getAcceptHeader(seed){
returnNotYetImplemented;
}
函数getAcceptCharsetHeader(种子){
返回NotYetImplemented;
}
函数getAcceptEncodingHeader(种子){
返回NotYetImplemented;

函数getAcceptLanguageHeader(){
//注意:TOR浏览器使用美式英语
returnen-US,en; q = 0.5;
}
函数getAuthorizationHeader(seed){
返回NotYetImplemented;
}
函数getExpectHeader(seed){
returnNotYetImplemented;
}
函数getFromHeader(seed){
returnNotYetImplemented;
}
function getHostHeader(seed){
returnNotYetImplemented;
}
函数getIfMatchHeader(种子){
返回NotYetImplemented;
}
函数getIfModifiedSinceHeader(seed){
returnNotYetImplemented;
}
函数getIfNoneMatchHeader(种子){
返回NotYetImplemented;
}
函数getIfRangeHeader(种子){
返回NotYetImplemented;
}
函数getIfUnmodifiedSinceHeader(seed){
returnNotYetImplemented;
}
函数getMaxForwardsHeader(seed){
returnNotYetImplemented;
}
函数getProxyAuthorizationHeader(seed){
returnNotYetImplemented;
}
函数getRangeHeader(seed){
返回NotYetImplemented;
}
函数getRefererHeader(){
//注意:从https://developer.mozilla.org/en-US/docs/Web/API/Document/referrer
//注意:如果用户直接导航到页面(不是通过链接,而是通过书签),则该值为空字符串。
//注意:由于此属性只返回一个字符串,因此它不会为您提供对引用页面的DOM访问权限。

//注意:让网站认为我们总是直接去他们,而不是被转介。
return;
}
函数getTEHeader(seed){
returnNotYetImplemented;
}
函数getUserAgentHeader(seed){
Math.seedrandom(seed);

返回userAgents [randomNumber(0,userAgents.length)];
}

函数rewriteHttpHeaders(e){
//从url创建URL对象
var serverUrl = new URL(e.url);

console.log(e);

//获取原点(主机名)
var origin = serverUrl.hostname;

var seed = getSeed(origin);

console.log(Background - Seed for origin+ origin +:+ seed);
$ b $(e.requestHeaders的var头){
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()===期望){
}
else if(header.name.toLowerCase()===from){
}
else if(header.name.toLowerCase() ===host){
}
else if(header.name.toLowerCase()===if-match){
}
else if .name.toLowerCase()===if-modified-since){
}
else if(header.name.toLowerCase()===if-none-match){$ (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()===用户代理){
header.value = getUserAgentHeader(seed);
}
}

return {requestHeaders:e.requestHeaders};


chrome.webRequest.onBeforeSendHeaders.addListener(rewriteHttpHeaders,{urls:[< all_urls>]},[blocking,requestHeaders]);

chrome.webNavigation.onBeforeNavigate.addListener(function(details){
//从url创建URL对象string
var serverUrl = new URL(details.url);

//获取原点(主机名)
var origin = serverUrl.hostname;

var seed =一些动态值;

console.log(Injecting Value);
chrome.tabs.executeScript(details.tabId,{code:var seed ='+ seed +'; console.log(seed);,runAt: document_start},function(){
console.log(Value Injected);
});
});

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

函数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 in here ???

injectText(var seed ='hello';);
console.log([INFO] Injected Seed ...);
inject(js / ua.js);
console.log([INFO] Injected UA ...);
inject(js / w ords.js);
console.log([INFO] Injected Words ...);
注入(js / lib / seedrandom.min.js);
console.log([INFO] Injected Seed Random ...);
注入(js / random.js);
console.log([INFO] Injected Random ...);
注入(js / api / document.js);
console.log([INFO] Injected Document API ...);
注入(js / api / navigator.js);
console.log([INFO] Injected Navigator API ...);
注入(js / api / canvas.js);
console.log([INFO] Injected Canvas API ...);
注入(js / api / history.js);
console.log([INFO] Injected History API ...);
注入(js / api / battery.js);
console.log([INFO] Injected Battery API ...);
注入(js / api / audio.js);
console.log([INFO] Injected Audio API ...);
注入(js / api / element.js);
console.log([INFO] Injected Element API ...);
})();

在尝试将种子记录到控制台时出现错误:

  inject.js:26 Uncaught ReferenceError:seed未定义
在inject.js:26
在inject.js:52

有什么想法?

解决方案<
Inject.js将需要访问此变量并在页面上的任何脚本运行之前运行它的代码。




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



Chrome有解决方案 - 您可以将执行设置为 document_start 。然后你的代码会真正在其他所有东西之前运行,而DOM仍然没有被解析(所以 document 本质上是空的)。使用你的代码所做的事情,它不应该产生问题(它只依赖于 document.documentElement ,它将存在)。



问题是,您的所有代码都必须是同步才能享受运行在其他所有内容之前属性。只要代码的同步部分运行,Chrome会暂停DOM解析,但随着它快速继续解析(并从中运行代码)文档,所有投注都将关闭。



<例如,这会使 chrome.storage 和Messaging失效,因为访问必须是异步的。


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

这意味着你不能预先存储这个在一些同步可用的存储中(例如在 localStorage 或网站的Cookie中),无论如何,考虑到您事先不知道域名,这可能会有问题。

b
$ b

请注意,对于您的代码而言,这可能不是那么重要;您的动态价值实际上是根据域来确定的。您仍然不会预先知道哪个域会被访问,但您至少可以保证在第二次加载时它会在那里。


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


这是棘手的部分。 事实上,正如所述,这是根本不可能的。您试图从后台捕获导航提交之间的确切时间,以便Chrome将页面切换到新域,并且执行您的 document_start 脚本。



没有可察觉的差距,无法告诉Chrome需要等待。这是一个竞争条件,你没有希望解决。



您正尝试使用 webNavigation.onBeforeNavigate - 之前即使是导航也承诺。因此,您的 injectScript 可能甚至会转到上一页,使其无效。如果您尝试其他事件,例如。 onCommitted ,仍然无法确切知道 injectScript 会被处理的时间。可能在你的脚本之后。



那么,如何解决这一切?



幸运的是,内容脚本可以使用一些同步存储,您可以在执行最早脚本之前将一些信息推送到



Cookie。



然而,使用 chrome.cookies API不会帮助。您需要在 webRequest.onHeadersReceived 中主动将cookie值注入到请求中。



必须具有该值准备同步 用阻塞处理程序处理它到 onHeadersReceived ,但是你可以简单地添加一个 Set-Cookie 标题,并立即在 inject.js 中的 document.cookies 中使用它。

  // background.js 
函数addSeedCookie(details){
seed = SomethingSynchronous();
details.responseHeaders.push({
name:Set-Cookie,
value:`seed_goes_here = $ {seed};`
});
return {
responseHeaders:details.responseHeaders
};
}

chrome.webRequest.onHeadersReceived.addListener(
addSeedCookie,{urls:[< all_urls>]},[blocking,responseHeaders]
);

// inject.js
函数getCookie(cookie){// https://stackoverflow.com/a/19971550/934239
return document.cookie.split(' ;')。reduce(function(prev,c){
var arr = c.split('=');
return(arr [0] .trim()=== cookie)?arr [1]:prev;
},undefined);
}

var seed = getCookie(seed_goes_here);

注意:上面的代码未经测试,仅用于说明这个想法。


Using my background script background.js, I need to inject a dynamic variable as a content script before injecting another file inject.js also as a content script. Inject.js will need to have access to this variable and run it's code BEFORE any scripts on the page run. I am having difficulties accessing the dynamic variable from the inject.js content script.

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"
]

}

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

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 ...");
})();

I get the error when trying to log the seed to the console:

inject.js:26 Uncaught ReferenceError: seed is not defined
    at inject.js:26
    at inject.js:52

Any ideas?

解决方案

This is going to be very tricky.

Let's look at your requirements.

Inject.js will need to have access to this variable and run it's code BEFORE any scripts on the page run.

That's not how your code currently works. Your inject.js is executed at document_end - which happens after the whole DOM tree is parsed, which means after all page scripts have run (barring asynchronous parts and async script loading).

Chrome has a solution to that - you can set your execution to document_start. Then your code will truly run before everything else, while DOM is still not parsed (so document is essentially empty). With what your code does, it should not create problems (it only relies on document.documentElement, which will exist).

Problem is, all your code has to be synchronous to still enjoy "runs before everything else" property. Chrome will pause DOM parsing as long as the synchronous part of your code runs, but then all bets are off as it merrily continues to parse (and run code from) the document.

This, for example, disqualifies chrome.storage and Messaging as access to that is necessarily asynchronous.

I need to inject a dynamic variable [on a page load]

Meaning that you cannot store this in advance in some synchronously-available storage (e.g. in localStorage or cookies of the website), which would be problematic anyway considering you don't know domains in advance.

Note, for your code in particular, this may not be that much of a factor; your "dynamic" value is in fact fixed per domain. You still don't know in advance which domain will be visited, but you can at least guarantee that on a second load it will be there.

Using my background script background.js, I need to inject a dynamic variable as a content script before injecting another file [that still needs to run before everything else on the page]

That's the tricky part. In fact, as stated, it's simply impossible. You're trying to catch, from the background, the exact moment between the navigation being committed, so that Chrome switched the page to the new domain, and the execution of your document_start script.

There is no detectable gap there, and no way to tell Chrome to wait. It's a race condition you have no hopes to resolve.

You're trying to use webNavigation.onBeforeNavigate - before even the navigation is committed. So your injectScript probably goes to the previous page even, rendering it useless. If you try some other event, e.g . onCommitted, there's still no telling as to when exactly injectScript will be treated. Likely after your script.

So, how to work around all this?

Fortunately, there is some synchronous storage that's available to the content script that you can push some information to right before the earliest of scripts executes.

Cookies.

However, using the chrome.cookies API won't help. You need to actively inject the cookie value into the request on webRequest.onHeadersReceived.

You have to have the value ready synchronously to process it with a blocking handler to onHeadersReceived, but then you can simply add one Set-Cookie header and have it immediately available in document.cookies in your inject.js.

// background.js
function addSeedCookie(details) {
  seed = SomethingSynchronous();
  details.responseHeaders.push({
    name: "Set-Cookie",
    value: `seed_goes_here=${seed};`
  });
  return {
    responseHeaders: details.responseHeaders
  };
}

chrome.webRequest.onHeadersReceived.addListener(
  addSeedCookie, {urls: ["<all_urls>"]}, ["blocking", "responseHeaders"]
);

// inject.js
function getCookie(cookie) { // https://stackoverflow.com/a/19971550/934239
  return document.cookie.split(';').reduce(function(prev, c) {
    var arr = c.split('=');
    return (arr[0].trim() === cookie) ? arr[1] : prev;
  }, undefined);
}

var seed = getCookie("seed_goes_here");

Note: the above code is untested and only serves to illustrate the idea.

这篇关于在内容脚本之前注入JavaScript变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆