New filtering in MouseHole
Kevin Ballard
kevin at sb.org
Sun Sep 4 05:11:54 EDT 2005
Ok, I've hacked on MouseHole a bit to make it possible for JavaScript
to have access to MouseHole. I've done it in a fairly restrictive
manner. Basically, I've made it possible for a script to handle any
URL whose path starts with /.mouseHole/, matching based on domain and
path. Because of naming issues, I decided to rename rewrite to filter
and call this new feature rewrite (after all, the existing rewrite is
really just filtering). But to preserve compatibility I've made
rewrite with no args act like the new filter.
How you use the new feature is you simply add a call to rewrite(). It
takes 2 args. The first is a regex to match against host:port, so you
might use /^(www\.)?google\.com:80/ to match against google. The
second is a regex to match against the path after /.mouseHole/, so to
match http://www.google.com/.mouseHole/ping you would use %r{^/ping
$}. On a request to the proxy, if the request method is GET/POST/PUT/
HEAD and the path starts with /.mouseHole/, it will match against all
existing rewriting, and the first one it finds that works it calls.
The rewrite block should set both req['Content-Type'] and req.body to
appropriate values. req['Content-Length'] will be filled in for you
based on req.body. The scripts are matched in whatever order
each_fresh_script uses and the individual rewrites will be matched in
order of definition. I would recommend that the path match be
something fairly unique, so multiple generic scripts (i.e. ones not
limited to a specific domain) don't try to use the same path. I
thought about sticking a script identifier into the matched path, but
I wasn't sure what to use.
The reason I restricted the rewriting to /.mouseHole/ is because it
makes writing scripts a bit easier (so you don't end up trying to
rewrite way too many pages when you meant to rewrite a specific page)
and faster (because it doesn't have to match all scripts for every
request). Also because this feature is meant primarily for JavaScript
XMLHttpRequest calls, although you could use this to toss in a
<script> or <img> tag or the like.
The patch can be found at <http://kevin.sb.org/files/
mousehole-1.patch>. It was created against CVS TOT right now (check
the email time if you care).
An example script is the following. It adds a new button to google's
index page that simply asks MouseHole for a random number. If you
don't care, do keep reading. I talk more after the script.
---------------
MouseHole.script do
# declaration
name "Rewriting Test"
namespace "kevin at sb.org"
description "Tests the new rewriting stuff."
include_match %r{^http://(www\.)?google\.com(/(index\.html)?)?$}
version "0.1"
filter do |req, res|
document.elements['//head'].add_element 'script', 'type' =>
'text/javascript', 'src' => '/.mouseHole/test.js'
lucky = document.elements['//input[@name="btnI"]']
lucky.parent.add_element 'input', 'type' => 'submit', 'value' =>
'Random', 'onclick' => 'pingMouseHole(); return false;'
end
rewrite(%r{^(www\.)?google\.com:80$}, %r{^/test\.js$}) do |uri,
req, res|
res['Content-Type'] = 'text/javascript'
res.body = <<-EOF
function createRequestObject() {
var ro;
var browser = navigator.appName;
if(browser == "Microsoft Internet Explorer"){
ro = new ActiveXObject("Microsoft.XMLHTTP");
}else{
ro = new XMLHttpRequest();
}
return ro;
}
var http = createRequestObject();
function sndReq(action, handler) {
http.open('get', action);
http.onreadystatechange = function() {
if(http.readyState == 4){
handler(http.responseText);
}
};
http.send(null);
}
function pingMouseHole() {
sndReq('/.mouseHole/ping', function(txt) {
alert(txt);
})
}
EOF
end
rewrite(%r{^(www\.)google\.com:80$}, %r{^/ping$}) do |uri, req, res|
res['Content-Type'] = 'text/plain'
res.body = uri.to_s + ": " + rand(100).to_s
end
end
-------------------
On Sep 5, 2005, at 2:31 AM, why the lucky stiff wrote:
> * Javascript access to MouseHole: A MouseHole script injects
> Javascript into a page and that Javascript needs to store data back
> inside MouseHole. Uhh, this exposes MouseHole to possible attack
> from other Javascripts. What APIs do we allow Javascript to
> access. (You don't want a rogue Javascript deactivating all your
> scripts or wiping out your configurations, right?)
Well, this provides the requested functionality. The only APIs
accessible from javascript are the ones that your user scripts
provide, so as long as they behave appropriately, there should be no
problem. Feel free to add some $SAFE magick'ry if you wish around
this, I don't have any experience twiddling $SAFE so I just ignored it.
> * Javascript cross-site access: A MouseHole script injects
> Javascript into a page and that Javascript needs to pass data to
> another domain (normally a JS failure.)
> ** So, if we're on boingboing and we want to access metafilter..
> http://boingboing.net/http://metafilter.com/api/post. That opens a
> security hole (the same one Greasemonkey just bumped up on.)
> ** Instead, we could have the MouseHole script register URLs before
> hand: register_url("http://metafilter.com/api/post")
> ** Then, the script is given a hash to access that domain. (Or is
> allowed access to it through http://boingboing.net/http://
> metafilter.com/api/post.)
> ** Or, just a whitelist with regexps.
With this new filtering, you just ask MouseHole to do the cross-
domain request. Only vulnerabilities are again what the script itself
provides.
Thoughts or comments?
Oh, and I don't suppose MouseHole could be hosted in an SVN repo
instead of a CVS repo? I'm using SVK to wrap it (because I hate using
CVS), but it's slow to sync changes in the repo.
--
Kevin Ballard
kevin at sb.org
http://www.tildesoft.com
http://kevin.sb.org
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 2378 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/mousehole-scripters/attachments/20050904/e8086156/smime.bin
More information about the Mousehole-scripters
mailing list