浏览器仍在泄漏真实用户代理

时间:2017-08-21 02:07:41

标签: javascript iframe google-chrome-extension sandbox

我正在编写一个隐私扩展,要求我欺骗浏览器的用户代理属性,也就是navigator.userAgent(是的,我已经知道了User-Agent HTTP标头并且已经处理过了)。

我的问题是,页面可能不只是主框架,也可能是iframe的变量。在我的清单文件中,我使用all_frames:true将我的内容脚本注入所有框架,并将match_about_blank:true注入到框架中,网址为“about:blank”。

我正在使用BrowserLeaks测试我的扩展程序,似乎使用window选项正确欺骗用户代理,但在使用iframe.contentWindow方法时,它会显示真正的用户代理。

我认为这可能是因为iframe是沙盒而且你不能注入沙盒iframe。这将是一个巨大的问题,因为网站可以逃避扩展并拒绝他们访问沙盒iframe。

这是我在Chromium上遇到的错误:

在'about:blank'中阻止执行脚本,因为文档的框架是沙箱,并且未设置'allow-scripts'权限。

来自Chrome Developer

match_about_blank:

可选。是否在about:blank和about:srcdoc上插入内容脚本。仅当内容脚本的继承URL与匹配字段中的一个声明模式匹配时,才会在页面上注入内容脚本。继承URL是创建框架或窗口的文档的URL。 内容脚本无法插入沙盒中。 默认为false。

或许脚本在所有iframe中运行,包括沙盒,但脚本运行速度不够快,即不是run_at:document_start。

来自MDN

match_about_blank:

Firefox版本52支持
match_about_blank。请注意,在Firefox中,即使您在run_at中指定了该值,内容脚本也不会注入“document_start”中的空iframe。

我的标题是chrome扩展名,但它也适用于Firefox。我发布了MDN和Chrome的文档,因为他们的措辞不同。在Chrome上,当我在github.com上测试这个时,我在iframe上遇到有关沙盒的错误,但是在Firefox上我没有这样的错误,但它仍然不会像我想要的那样欺骗iframe中的属性。有什么想法吗?

的manifest.json

{
    "name": "Shape Shifter",
    "version": "0.0.1",
    "description": "Anti browser fingerprinting web extension. Generates randomised values for HTTP request headers and javascript API's.",
    "manifest_version": 2,
    "icons": {
        "16": "icons/crossed_eye_16x16.png",
        "32": "icons/crossed_eye_32x32.png",
        "48": "icons/crossed_eye_48x48.png",
        "128": "icons/crossed_eye_128x128.png"
    },
    "background": {
        "persistent": true,
        "scripts": ["js/background.js"]
    },
    "browser_action": {
        "default_title": "Shape Shifter",
        "default_icon": {
            "16": "icons/crossed_eye_16x16.png",
            "32": "icons/crossed_eye_32x32.png"
        },
        "default_popup": "html/popup.html"
    },
    "content_scripts": [
        {
            "all_frames": true,
            "match_about_blank": true,
            "run_at": "document_end",
            "matches": ["<all_urls>"],
            "js": ["js/inject.js"]
        }
    ],
    "permissions": [
        "webRequest",
        "webRequestBlocking",
        "<all_urls>"
    ],
    "web_accessible_resources": [
        "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(我的内容脚本)

console.log("Content Script Running ...");

function inject(filePath, seed) {
  // Dynamically create a script
  var script = document.createElement('script');

  // Give the script a seed value to use for spoofing
  script.setAttribute("data-seed", seed);

  // Give the script a url to the javascript code to run
  script.src = chrome.extension.getURL(filePath);

  // Listen for the script loading event
  script.onload = function() {
    // Remove the script from the page so the page scripts don't see it
    this.remove();
  };

  // Add the script tag to the DOM
  (document.head || document.documentElement).appendChild(script);
}

function getSeed(origin) {
    // Get a Storage object
    var storage = window.sessionStorage;

    // Try to get a seed from sessionStorage
    var seed = storage.getItem(origin);

    // Do we already have a seed in storage for this origin or not?
    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;
}

var seed = getSeed(window.location.hostname);

inject("js/lib/seedrandom.min.js", seed);
console.log("[INFO] Injected Seed Random ...");

inject("js/random.js", seed);
console.log("[INFO] Injected Random ...");

inject("js/api/document.js", seed);
console.log("[INFO] Injected Document API ...");

inject("js/api/navigator.js", seed);
console.log("[INFO] Injected Navigator API ...");

inject("js/api/canvas.js", seed);
console.log("[INFO] Injected Canvas API ...");

inject("js/api/history.js", seed);
console.log("[INFO] Injected History API ...");

inject("js/api/battery.js", seed);
console.log("[INFO] Injected Battery API ...");

inject("js/api/audio.js", seed);
console.log("[INFO] Injected Audio API ...");

inject("js/api/element.js", seed);
console.log("[INFO] Injected Element API ...");

1 个答案:

答案 0 :(得分:0)

我能够通过以下方式解决这个问题:

从我的内容脚本...

var UAScript = "document.addEventListener('DOMContentLoaded', function(event) { var iFrames = document.getElementsByTagName('iframe'); "+
               "for (i=0; i<iFrames.length; i++) try { Object.defineProperty(iFrames[i].contentWindow.clientInformation, 'userAgent', { value:'Custom' }); } catch(e) {} }); "+
               "try { Object.defineProperty(clientInformation, 'userAgent', { value:'Custom' }); } catch(e) {}";
var script = document.createElement("script");
script.type = "text/javascript";
script.textContent = UAScript;
document.documentElement.appendChild(script);

这会更改文档上的userAgent以及DOMContentLoaded触发后的iFrame。

问题是......,有更好的方法吗?

(因为你仍然可以通过在iframe中添加iframe等来解决这个恶搞。)