[tor-dev] HTTP-requesting browser extension WIP (works in Firefox, not in Tor Browser)

David Fifield david at bamsoftware.com
Tue Mar 11 03:23:35 UTC 2014


On Fri, Feb 21, 2014 at 08:08:59PM -0800, David Fifield wrote:
> We're making progress on meek (https://trac.torproject.org/projects/tor/wiki/doc/meek),
> the transport that hides your traffic in HTTPS requests to an
> unblockable web site. It's already doing a good job at reliably
> transfering bits. What we'd like to do next is remove trivial means of
> blocking it based on network fingerprinting. We're currently using the
> golang HTTPS library, and at https://trac.torproject.org/projects/tor/wiki/doc/meek#Distinguishability
> you can see meek's TLS differs from Chromium's in its ciphersuites and
> extensions.
> 
> It seems the right thing to do is mimic a browser, and I can think of at
> least three ways to do that:
>  1. Try really hard, using NSS or some other library, to look like a
>     particular browser.
>  2. Run a second browser, apart from Tor Browser, that receives commands
>     from a client PT program and makes the HTTPS requests it is
>     commanded to.
>  3. Run a browser plugin *inside* Tor Browser, that makes HTTPS requests
>     *directly on the Internet, without going through Tor*. That is, the
>     plugin receives commands from the client PT program, and then
>     bypasses all of Tor Browser's proxy settings in order to send HTTPS
>     requests to the web site fronting the circumvention.
> 
> It's the third option I want to ask about. The first option puts us on
> the parrot treadmill. The second has the usability and distribution
> problems of running two browsers at once. The third is slightly crazy,
> but more usable and easier to deploy. The third option is definitely
> what I would do if I were using a browser other than Tor Browser and
> designing purely for circumvention and not anonymity.

I started trying to write a Firefox extension that makes HTTP requests
outside of the proxy settings. I have one that works in Iceweasel 24.3
and does the Host header trick used by the transport. However it doesn't
work in Tor Browser, and I'm looking for some insight as to why it might
be so.

The source code of the extension is in the "firefox" directory of
	git clone -b extension https://www.bamsoftware.com/git/meek.git
Instructions on how to try it are: https://developer.mozilla.org/en-US/docs/Building_an_Extension#Test.
I also pasted the important JavaScript code at the end of this message.

The extension just makes an HTTPS request when you start the browser.
The request is apparently for https://www.google.com/ but it actually
goes to https://meek-reflect.appspot.com/. In Iceweasel with window.dump
enabled, this is the output (in addition Wireshark shows me the request
is happening):

*** LOG addons.xpi: startup
*** LOG addons.xpi: checkForChanges
*** LOG addons.xpi-utils: Opening database
*** LOG addons.xpi: Add-on meek-http-helper at bamsoftware.com modified in app-profile
*** LOG addons.xpi: Updating database with changes to installed add-ons
*** LOG addons.xpi-utils: Updating add-on states
*** LOG addons.xpi-utils: Writing add-ons list
onStartRequest
onDataAvailable
38:[object Uint8Array]
onStopRequest

"onStartRequest", "onDataAvailable", and "onStopRequest" are output from
the extension. 38 is the length of the "I’m just a happy little web server.\n"
response from the bridge. I configured a proxy in Iceweasel and the
request bypasses the proxy. Everything looks right.

Here is the output when I run in TBB 3.5.2.1. There's "onStartRequest"
and "onStartRequest" but no "onDataAvailable" with the data. Wireshark
doesn't show any HTTPS request happening.

*** LOG addons.xpi: startup
*** LOG addons.xpi: checkForChanges
*** LOG addons.xpi-utils: Opening database
*** LOG addons.xpi: Add-on meek-http-helper at bamsoftware.com modified in app-profile
*** LOG addons.xpi: Updating database with changes to installed add-ons
*** LOG addons.xpi-utils: Updating add-on states
*** LOG addons.xpi-utils: Writing add-ons list
no loadgroup notificationCallbacks for https://www.google.com/
onStartRequest
onStopRequest

"no loadgroup notificationCallbacks" is a string from HTTPS Everywhere.
Any ideas as to why it doesn't work? I've never written a browser
extension before so I'd appreciate tips on how to approach debugging it.

David Fifield


var FRONT_URL = "https://www.google.com/";
var HOST = "meek-reflect.appspot.com";

// Create a "direct" nsIProxyInfo that bypasses the default proxy.
// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIProtocolProxyService
var pps = Components.classes["@mozilla.org/network/protocol-proxy-service;1"]
	.getService(Components.interfaces.nsIProtocolProxyService);
// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIProxyInfo
var proxy = pps.newProxyInfo("direct", "", 0, 0, 0xffffffff, null);

// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIIOService
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
	.getService(Components.interfaces.nsIIOService);
var httpProtocolHandler = ioService.getProtocolHandler("http")
	.QueryInterface(Components.interfaces.nsIHttpProtocolHandler);
var uri = ioService.newURI(FRONT_URL, null, null);
// Construct an HTTP channel with the proxy bypass.
// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIHttpChannel
var channel = httpProtocolHandler.newProxiedChannel(uri, proxy, 0, null)
	.QueryInterface(Components.interfaces.nsIHttpChannel);
// Set the host we really want.
channel.setRequestHeader("Host", HOST, false);
channel.redirectionLimit = 0;
// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIUploadChannel
// channel.requestMethod = "POST";

var listener = new StreamListener();
channel.asyncOpen(listener, null);

// https://developer.mozilla.org/en-US/docs/Creating_Sandboxed_HTTP_Connections
function StreamListener() {
	this.onStartRequest = function(aRequest, aContext) {
		console.log("onStartRequest\n");
		dump("onStartRequest\n");
	};
	this.onStopRequest = function(aRequest, aContext, aStatus) {
		dump("onStopRequest\n");
	};
	this.onDataAvailable = function(aRequest, aContext, aStream, aSourceOffset, aLength) {
		dump("onDataAvailable\n");
		var a = new Uint8Array(aLength);
		var input = Components.classes["@mozilla.org/binaryinputstream;1"]
			.createInstance(Components.interfaces.nsIBinaryInputStream);
		input.setInputStream(aStream);
		input.readByteArray(aLength, a);
		dump(aLength + ":" + a + "\n");
	};
}


More information about the tor-dev mailing list