• Home
  • Quick Start
    • Configurator
    • Download YUI 3
  • Documentation
    • User Guides
    • Examples
    • Tutorials
    • API Docs
  • Community
    • Gallery
    • Blog
    • Forums
    • YUI Theater
    • Calendar
  • Contribute
    • YUI on GitHub »
    • File a Ticket
    • View Tickets
    • Dashboard
  • Other Projects
    • YUI 2
    • YUI Compressor
    • YUI Doc »
    • YUI Builder
    • YUI PHP Loader
    • YUI Test
    • YUI Website

Blog: Archive for April, 2007

« Older Entries

Free Excerpt — Web Development Solutions: Ajax, APIs, Libraries, and Hosted Services Made Easy, by Christian Heilmann and Mark Norman Francis

Christian Heilmann and Mark Norman Francis are lead Yahoo! frontend engineers who ply their trade in Yahoo!’s London office in Covent Garden. Both are frequent speakers on the web-developer conference circuit in the UK. Christian blogs at http://wait-till-i.com; Norm writes insighfully (if only occasionally) at http://cackhanded.net/.

Their new book from Friends of Ed (APress) is Web Development Solutions: Ajax, APIs, Libraries, and Hosted Services Made Easy. The purpose of new volume was to show to people who want to publish on the web that by using APIs and hosted services it is possible to set up a complex and sophisticated web application without having to reinvent every technical wheel along the way. Here’s how Christian explains it:

Let Flickr worry about the conversion of your photos, del.icio.us allow you to add up-to-date links and WordPress and its developer community help you pick a template. All you should really worry about is the content you want to offer your visitors.

As is our wont when new books come out with YUI-oriented content, we asked Friends of Ed editor Chris Mills if we might share an excerpt here. He was kind enough to provide a section of the book in which Christian and Norm step through the creation of hierarchical menus and the use of animation with the YUI library.

  • Download free excerpt from Web Development Solutions (225KB zipped PDF)
By Eric MiragliaApril 30th, 2007

I’d Rather switch Than Fight!

JavaScript’s switch statement was inspired by Java’s switch statement, which was inspired by C++’s switch statement, which was inspired by C’s switch statement, which combined aspects of C. A. R. Hoare’s case statement and Fortran’s computed goto statement. Dijkstra considered the goto statement to be harmful, which it in fact is, which is why the goto has been omitted from most modern programming languages. But some of the goto‘s problematic nature survives in the switch, so some extra care must be employed when using it.

The problem with switch was once thought to be its cleverest feature: Fallthru. If you do not explicitly interrupt the flow of control, after executing the code associated with a case, the code of the next case is run too. This provides for a sort of local code reuse, but it can make programs that are harder to read, and it can mask bugs. Accidental fallthru bugs can be very expensive to identify and fix. The best defense against them is to never intentionally use fallthru. That way, if a fallthru ever appears in your code, you can immediately recognize it as a defect that needs correction. That is a lot easier than trying to figure out which fallthrus are accidental and which are not.

Features like fallthru are attractive nuisances. They are sometimes useful, but they encourage weak coding patterns. JavaScript has more than its share of attractive nuisances (with, implied globals, new, and semicolon insertion, to name a few). Your programs will be stronger if you avoid them.

JavaScript’s switch is better than Java’s because it allows matching on strings. This is really useful. The ability to switch on strings satisfies the role that enum provides in other languages. Being able to do more with less is a mark of a good programming language.

JavaScript matches its cases with the === operator, which tests for equality without type coercion. (The == operator does type coercion, which sometimes produces surprising results. It is another attractive nuisance.) The value NaN cannot be found to be equal to itself, so

case NaN:

will never match.

By Douglas CrockfordApril 25th, 2007

YUI Tutorial: Subclassing DataTable to Create DataView

Victor Morales is a software engineer at DePaul University. Originally from Mexico, Victor’s passion for technology began after learning programming by himself on an Apple IIe computer at age 10. He recently became interested in JavaScript as a development platform, and he’s been one of the most active contributors lately to the YUI developer forums.

In this YUI Blog tutorial, Victor explores the process of integrating the new YUI DataTable with the ContextMenu class of the YUI Menu Control as well as with the AutoComplete Control.

In this tutorial we will build a subclass for DataTable called DataView. This subclass will allow the DataTable to hide a particular column by right clicking in the table header rows and selecting a column from a ContextMenu — one of the major types of menus supported by the YUI Menu Control.

The first step to subclassing DataTable is writing the constructor for the new subclass (DataView, in this case) and specifying its inheritance from the DataTable class using YAHOO.extend:

 //create namespace:
 YAHOO.namespace("yuiblog.widget");

  YAHOO.yuiblog.widget.DataView = function(elContainer , oColumnSet , oDataSource , oConfigs) {
         if (arguments.length > 0) {
                YAHOO.yuiblog.widget.DataView.superclass.constructor.call(this, elContainer , oColumnSet , oDataSource , oConfigs);
           }
           //Call ContextMenu initialization method
           this._initHideMenu();
 };
 // Inherit from YAHOO.widget.DataTable
 YAHOO.lang.extend(YAHOO.yuiblog.widget.DataView, YAHOO.widget.DataTable);
 

The DataView constructor adds a hookup to the _initHideMenu method, which initializes the ContextMenu. This method has the following responsibilites:

  1. Initialize and create a ContextMenu instance.
  2. Determine if a column can be hidden, and if so add it as an item in the ContextMenu.
  3. Subscribe each MenuItem to the onhideMenuClick event handler.
 YAHOO.yuiblog.widget.DataView.prototype._initHideMenu=function(oColumnSet) {

         var oColumnSet= this._oColumnSet
         this.aColState=[];
         var _hideCol=[]
         var keys= oColumnSet.keys;
         for (var i=0; i<keys.length;i++) {
             if(keys[i].hideable) {
                     itemText = keys[i].text || keys[i].key;
                 _hideCol.push({text:itemText,checked:true, colNum:i})
              }
             this.aColState[i]=0; 
         }
         if (_hideCol.length>0)    {
           var oContextMenu = new YAHOO.widget.ContextMenu("hideMenu", { trigger: this.getHead()     } );

           // Define the items for the menu
           var aMenuItemData =_hideCol 
           var nMenuItems = aMenuItemData.length;
           var oMenuItem;
           for(var i=0; i<nMenuItems; i++) {
              var item= aMenuItemData[i]
              oMenuItem = oContextMenu.addItem(item);
              oMenuItem.clickEvent.subscribe(this.onhideMenuClick, [oMenuItem,item.colNum],this);
           }
           oContextMenu.render(document.body);
         }

 }; 

Notice how _initHideMenu iterates over the ColumnSet keys array, which maps one-to-one to a table column. If a column has the hideable property set to true, an anonymous object is created with the column text and position and then “pushed” into an array that is used to populate the ContextMenu.

The next step is to define the onHideMenuClick method, which hides the appropriate column from the DataTable depending on the MenuItem that was clicked. To hide a column we simply call the hideSwap method which alters the display attribute of the column.

 YAHOO.yuiblog.widget.DataView.prototype.onhideMenuClick=function(p_sType, p_aArgs, p_oMenuItem) {
         var oMenuItem= p_oMenuItem[0];
         var col_no=p_oMenuItem[1];
         var swap= oMenuItem.cfg.getProperty("checked")
         oMenuItem.cfg.setProperty("checked", swap);
         var colstyle;
         if (!swap) {
             this.hideSwap(col_no,'none',0)
             this.aColState[col_no]=1      
         }
         else {
             this.hideSwap(col_no,'',0)
             this.aColState[col_no]=0
         }
 };
 YAHOO.widget.DataView.prototype.hideSwap=function(col_no,colstyle,startRow) {
       //Hide or unhide column header
        var headRow= this.getHead().getElementsByTagName('th')
        headRow[col_no].style.display=colstyle;

        var rows= this.getBody().getElementsByTagName('tr')

        // Hide or unhide column rows 
        for (var row=startRow; row<rows.length;row++) {
          var cels = rows[row].getElementsByTagName('td')
          cels[col_no].style.display=colstyle;
        }
};
 

With these changes in place, in our HTML page we only need to make sure to specify which columns will be “hideable”.

        var myColumnHeaders = [
             {key:"POID", abbr:"Purchase order ID", sortable:true, resizeable:true },
             {key:"Date", type:"date", sortable:true, resizeable:true, hideable:true},
             {key:"Quantity", type:"number", sortable:true, resizeable:true,hideable:true},
             {key:"Amount", type:"currency", sortable:true, resizeable:true,hideable:true},
             {key:"Title", text:"Book Title", type:"string", sortable:true, resizeable:true,hideable:true}
         ]; 

Click here for a functional example of the the project at this stage.

Adding Filtering to the DataView

Sometimes it is useful to view only a particular type of information and hide the rest. Filtering data from a table is a simple yet powerful pattern that allows users to find the information that they want in less time. Here we will leverage the AutoComplete Control and combine it with DataTable.

We’ll continue building upon the DataView class described above. Let’s take a quick look at the methods and properties needed to implement row filtering in our DataView class:

Name Responsibility Type
defaultView Store the original (unfiltered) table records Array property
isFiltered Keeps track of the state of the table boolean property
doBeforeLoadData Populates the defaultView array method (overriden)
filterRows Updates the content of the table method


The corresponding code is shown below:

 YAHOO.yuiblog.widget.DataView.prototype.isFiltered=false;

  YAHOO.yuiblog.widget.DataView.prototype.doBeforeLoadData= function( sRequest ,oResponse ) {
     if(oResponse) {
         this.defaultView=oResponse;
     }    
     return true;
 }

  YAHOO.yuiblog.widget.DataView.prototype.filterRows=function(filteredRows) {
     if(filteredRows == undefined) {
         this._oRecordSet.replace(this.defaultView);
         this.populateTable();
         this.isFiltered=false;
     }
     else {
         var dataView=[];
         for (var i=0; i<filteredRows.length;i++) {
              var r=filteredRows[i];
              var row= this._oRecordSet._records[r];
              dataView.push(row);
           }
           this.replaceRows(dataView);
           this._oRecordSet._records=dataView;
           this.isFiltered=true;
     }
 }; 

To initialize the defaultView property we take advantage of the doBeforeLoadData method which is automatically called by the DataView constructor once data is available.

Slightly more interesting is the filterRows method. This method receives as a parameter an array containing the row numbers that will be displayed. If we don’t specify an array then the DataView is reset to its default state and the isFiltered property is set to false.

That is really all we have to do for the DataView class. The next step is to create a subclass of AutoComplete, RowFilter, which will be responsible for "feeding" the filterRows method we just created:

 YAHOO.yuiblog.widget.RowFilter = function( elInput,elContainer,oDataTable,fnFilter,oConfigs) {
         if (arguments.length > 0) {
                YAHOO.yuiblog.widget.RowFilter.superclass.constructor.call(this, elInput,elContainer,fnFilter,oConfigs);
           }

         this.Filter=fnFilter;
         this._oDataTable=oDataTable;
          this.itemSelectEvent.subscribe(this.myOnSelect);
         this.dataReturnEvent.subscribe(this.myOnDataReturn);
         this._oDataTable.subscribe("columnSortEvent",this.updateFilter,this._oDataTable,this)
 }

                 // Inherit from YAHOO.widget.RowFilter
 YAHOO.lang.extend(YAHOO.yuiblog.widget.RowFilter, YAHOO.widget.AutoComplete); 
     

The core of the RowFilter class are the methods myOnSelect, myOnDataReturn and updateFilter. Again, a table summarizing their roles would be helpful:

Name Responsibility
myOnSelect Calls the filterRows method of its DataView instance when the user selects a result
myOnDataReturn Check if its DataView instance is filtered. If true, then it resets its DataSource and DataView instances to their original state
UpdateFilter Updates its DataSource to match the sorted DataTable

Here’s the code for each of these pieces:

myOnSelect:

 YAHOO.yuiblog.widget.RowFilter.prototype.myOnSelect= function(sType, aArgs) {
      var objResult = aArgs[2][1];
     this._oDataTable.filterRows(objResult.matchedRows)
 }
 

myOnDataReturn:

 YAHOO.yuiblog.widget.RowFilter.prototype.myOnDataReturn= function(sType, aArgs) {
      var oAutoComp = aArgs[0];
      var sQuery = aArgs[1];
      var aResults = aArgs[2];

     if(aResults.length == 0) {
           oAutoComp.setBody("<div id=\"container_default\">No matching results</div>");
      }

     this.reset();
 } 

UpdateFilter:

 YAHOO.yuiblog.widget.RowFilter.prototype.updateFilter=function(oColumn,oDataTable) {
      var records=oDataTable.getRecordSet().getRecords();
      this.Filter._aData=records;
      if (oDataTable.isFiltered) {
          this.hideColumns();
      }
 }; 

When I said that the RowFilter class is responsible for feeding the filterRows method I lied. In reality, the heavy lifting is delegated to the fnFilter method of the StringFilter class.

StringFilter Constructor:

 YAHOO.yuiblog.util.StringFilter=function(aRecords, sFieldName, oConfigs) {
       if(typeof oConfigs == "object") {
         for(var sConfig in oConfigs) {
             this[sConfig] = oConfigs[sConfig];
         }
     }
      this._aData=aRecords;
      this.schemaItem=sFieldName;
      this._init();
 };

  YAHOO.yuiblog.util.StringFilter.prototype = new YAHOO.widget.DataSource(); 

fnFilter method:

 YAHOO.yuiblog.util.StringFilter.prototype.fnFilter=function(sQuery) {
      sQuery=unescape(sQuery);
      var aResults = [];
      var aData= this._aData;
      var fName= this.schemaItem;
      if(sQuery && sQuery.length > 0) {
           var q= sQuery.toLowerCase();
           var updateResult=false;
           var elHashTable={}

           for (var i=0; i<aData.length; i++) {
                var field=aData[i][fName];
                var updateResult=false;

                 if(elHashTable[field]) {
                     //Update Hashtable entry with the additional row matched 
                      elHashTable[field].rows.push(i)
                      updateResult=true;
                }
                else {
                      elHashTable[field]= {rows:[i], resultIndex:-1};
                }

                //Save the index of the match
                var mIndex=field.toLowerCase().indexOf(q);
                var objResult={value:field, matchIndex:mIndex, matchedRows:[i]
           }

                                 if (mIndex<0) { continue;}

                                  if(updateResult){
                     var ri = elHashTable[field].resultIndex;
                     objResult.matchedRows=elHashTable[field].rows;
                     aResults[ri]=[objResult.value,objResult];
                }
                else {
                       aResults.push([objResult.value,objResult]);  
                       //Update the hashtable resultIndex   
                       elHashTable[field].resultIndex = aResults.length-1;
                       var ri= elHashTable[field].resultIndex;
                }
           }
      }
      return aResults;
 } 

The StringFilter class implements the DataSource interface and has two important properties: _aData which is a reference to the unfiltered records, and schemaItem which maps to a field name in the DataTable. When the fnFilter method receives a query from the RowFilter class it looks for the query term in the schemaItem column of its _aData array. This method returns an array containing the content of the column rows that match the query, as well as their corresponding row numbers.

Click here to try out the full DataView example including the ContextMenu and AutoComplete integration running on YUI version 2.3.1. (An older version running on version 2.2.2 is available here.)

Note:For the sake of simplicity, this particular hideColumns implementation does not work with nested headers.

[Update] Fixed display bug when user hides a column after applying a filter.

By Victor MoralesApril 23rd, 2007

YUI Version 2.2.2: Bug-Fix Release

Download YUIWe released version 2.2.2 of the Yahoo User Interface Library (YUI) today. This is a minor bug-fix release that corrects several issues introduced in the 2.2.1 update. Full details are available in the README files that accompany the download. For a short overview, please review the release note available in the YUI Forums.

By Eric MiragliaApril 18th, 2007

YUI Theater — John Resig, “Advancing JavaScript with Libraries”

John Resig, Mozilla Corp.

John Resig, creator of the JQuery JavaScript library and author of Pro JavaScript Techniques, is a Mozilla Corp. technologist focused on the relationship between Mozilla and the world of JavaScript libraries. In that capacity, he’s embarked on some creative and compelling initiatives, including an effort to work with JavaScript library developers to help "future-proof" upcoming releases of Firefox by integrating those libraries’ test suites within the larger Mozilla QA process.

I’ve been impressed with the degree to which John’s work on JQuery shows an empathy for (and understanding of) his users, so I was gratified that he agreed to visit Yahoo! to give a broader talk about the role of JavaScript libraries within the web-development discipline. He was kind enough to let us record his session and share it as part of the YUI Theater series. Dion Almaer stopped by and wrote up some comprehensive notes on Ajaxian. I won’t try to replicate those here, but I do want to mention that John’s articulation of his development philosophy in this presentation goes a long way toward explaining the success that JQuery has enjoyed among developers. Embracing web developers, even those who might be "writing their first lines of code" using JQuery, has led John toward idioms that will feel comparatively natural and intuitive for someone coming from the world of CSS and markup rather than the world of C++ or EJBs. It was fun listening to John talk through that philosophy.

To conform to Yahoo! Video’s 100MB file-size limits, the hour-long talk is divided into two parts (Part One; Part Two). Part One is embedded below:

Note: This video is also available as a download in .m4v (MPEG-4) format that is both iPod-compatible and playable on almost any OS. In some cases, you may find that renaming the file to .mp4 helps your player recognize the format.

In Case You Missed…

Some other recent videos from the YUI Theater series:

  1. Doug Geoffray: "From the Mouth of a ScreenReader" (Yahoo! Video | .m4v download)
  2. Gopal Venkatesan: "Efficient JavaScript" (Yahoo! Video
  3. Joe Hewitt: "Welcome to Firebug 1.0 " (Yahoo! Video | .m4v download)
  4. Douglas Crockford, MC: "Browser Wars II: Attack of the DOMs" (Yahoo! Video | .m4v download)

Much more content, including Douglas’s well-received series of lectures on JavaScript, is available on the YUI Theater website.

By Eric MiragliaApril 16th, 2007

Performance Research, Part 4: Maximizing Parallel Downloads in the Carpool Lane

This article, co-written by Steve Souders, is the fourth in a series of articles describing experiments conducted to learn more about optimizing web page performance (Part 1, Part 2, Part 3). You may be wondering why you’re reading a performance article on the YUI Blog. It turns out that most of web page performance is affected by front-end engineering, that is, the user interface design and development.

Parallel Downloads

The biggest impact on end-user response times is the number of components in the page. Each component requires an extra HTTP request, perhaps not when the cache is full, but definitely when the cache is empty. Knowing that the browser performs HTTP requests in parallel, you may ask why the number of HTTP requests affects response time. Can’t the browser download them all at once?

The explanation goes back to the HTTP/1.1 spec, which suggests that browsers download two components in parallel per hostname. Many web pages download all their components from a single hostname. Viewing these HTTP requests reveals a stair-step pattern, as shown in Figure 1.

Figure 1. Downloading 2 components in parallel

Figure 1. Downloading 2 components in parallel

If a web page evenly distributed its components across two hostnames, the overall response time would be about twice as fast. The HTTP requests would look as shown in Figure 2, with four components downloaded in parallel (two per hostname). The horizontal width of the box is the same, to give a visual cue as to how much faster this page loads.

Figure 2. Downloading 4 components in parallel

Figure 2. Downloading 4 components in parallel

Limiting parallel downloads to two per hostname is a guideline. By default, both Internet Explorer and Firefox follow the guideline, but users can override this default behavior. Internet Explorer stores the value in the Registry Editor. (See Microsoft Help and Support.) Firefox’s setting is controlled by the network.http.max-persistent-connections-per-server setting, accessible in the about:config page.

It’s interesting to note that for HTTP/1.0, Firefox’s default is to download eight components in parallel per hostname. Figure 3 shows what it would look like to download these ten images if Firefox’s HTTP/1.0 settings are used. It’s even faster than Figure 2, and we didn’t have to split the images across two hostnames.

Figure 3. Downloading 8 components in parallel

Figure 3. Downloading 8 components in parallel

Most web sites today use HTTP/1.1, but the idea of increasing parallel downloads beyond two per hostname is intriguing. Instead of relying on users to modify their browser settings, front-end engineers could simply use CNAMEs (DNS aliases) to split their components across multiple hostnames. Maximizing parallel downloads doesn’t come without a cost. Depending on your bandwidth and CPU speed, too many parallel downloads can degrade performance.

If browsers limit the number of parallel downloads to two (per hostname over HTTP/1.1), this raises the question:

What if we use additional aliases to increase parallel downloads in our pages?

We’ve seen a couple great blogs and articles written recently on the subject, most notably Ryan Breen of Gomez and Aaron Hopkins over at Google. Here’s another spin. The performance team at Yahoo! ran an experiment to measure the impact of using various numbers of hostname aliases. The experiment measured an empty HTML document with 20 images on the page. The images were fetched from the same servers as those used by real Yahoo! pages. We ran the experiment in a controlled environment using a test harness that fetches a set of URLs repeatedly while measuring how long it takes to load the page on DSL. The results are shown in Figure 4.

Figure 4. Loading an Empty HTML Document with 20 images using Various Number of Aliases

Figure 4. Loading an Empty HTML Document with 20 images using Various Number of Aliases

Note: Times are for cached aliases, empty file cache page loads on DSL (~800 kbps).

In our experiment, we vary the number of aliases: 1, 2, 4, 5, and 10. This increases the number of parallel downloads to 2, 4, 8, 10, and 20 respectively. We fetch 20 smaller-sized images (36 x 36 px) and 20 medium-sized images (116 x 61 px). To our surprise, increasing the number of aliases for loading the medium-size images (116 x 61px) worsens the response times using four or more aliases. Increasing the number of aliases by more than two for smaller-sized images (36 x 36px) doesn’t make much of an impact on the overall response time. On average, using two aliases is best.

One possible contributor for slower response times is the amount of CPU thrashing on the client caused by increasing the number of parallel downloads. The more images that are downloaded in parallel, the greater the amount of CPU thrashing on the client. On my laptop at work, the CPU jumped from 25% usage for 2 parallel downloads to 40% usage for 20 parallel downloads. These values can vary significantly across users’ computers but is just another factor to consider before increasing the number of aliases to maximize parallel downloads.

These results are for the case where the domains are already cached in the browser. In the case where the domains are not cached, the response times get significantly worse as the number of hostname aliases increases. For web pages desiring to optimize the experience for first time users, we recommend not to increase the number of domains. To optimize for the second page view, where the domains are most likely cached, increasing parallel downloads does improve response times. The choice depends on which scenario was most typical.

Another issue to consider is that DNS lookup times vary significantly across ISPs and geographic locations. Typically, DNS lookup times for users from non-US cities are significantly higher than those for users within the US. If a good percentage of your users are coming from outside the US, the benefits of increasing parallel downloads is offset by the time to make many DNS lookups.

Our rule of thumb is to increase the number of parallel downloads by using at least two, but no more than four hostnames. Once again, this underscores the number one rule for improving response times: reduce the number of components in the page.

Steve Souders is also writing a series of blogs on Yahoo! Developer Network describing best practices he’s developed at Yahoo! for improving performance (Part 1, Part 2).

By Tenni TheurerApril 11th, 2007

JSON and Browser Security

JSON is a data interchange format. It is used in the transmission of data between machines. Since it carries only data, it is security-neutral. The security of systems that use JSON is determined by the quality of the design of those systems. JSON itself introduces no vulnerabilities.

The web browser is a peculiar application environment. The security model of the browser was forged through a long series of foreseeable and painful blunders. Most of the holes in the browser have been plugged, but in some cases the plugs become annoyances which must be circumvented, and that circumvention leads, foreseeably and ever-painfully, to a continuing series of blunders.

This pain can be avoided by adopting good practices. Often, so-called experts seem to be incapable of distinguishing the good practices from the bad ones, so there is a lot of bad advice available on the web.

I will share here a small set of principles which can be seen to be true. If you hold to these principles, you will be much less likely to adopt bad practices.

Never Trust The Browser

The browser cannot and will not protect your secrets, so never send it your secret sauce. Keep your critical processes on the server. If you are doing input validation in the browser, it is for the user’s convenience. Everything that the browser sends to the server must be validated.

Keep Data Clean

JSON is a subset of JavaScript, which makes it especially easy to use in web applications. The eval function can take a text from XMLHttpRequest and instantly inflate it into a useful data structure. Be aware however that the eval function is extremely unsafe. If there is the slightest chance that the server is not encoding the JSON correctly, then use the parseJSON method instead. The parseJSON method uses a regular expression to ensure that there is nothing dangerous in the text. The next edition of JavaScript will have parseJSON built in. For now, you can get parseJSON at http://www.JSON.org/json.js.

On the server side, always use good JSON encoders and decoders.

Script Tags

Script tags are exempt from the Same Origin Policy. That means that any script from any site can potentially be loaded into any page. There are some very important consequences of this.

Any page that includes scripts from other sites is not secure. External scripts can be used to deliver ads or search result options, or logging, or alerts, or buddy lists, or lots of other nice things. Unfortunately, the designs of JavaScript and the DOM did not anticipate such useful services, so they offer absolutely no security around them. Every script on the page has access to everything on the page. When you load a script on a page, you are authorizing that script to have access to all of your confidential information and all of your user’s confidential information. You are also authorizing that script to access your server on the user’s behalf to do anything it wants. It is not possible to distinguish between requests made by the user and requests made by an invited script. Hopefully, some day soon, browsers will offer some degree of modularity that would limit the danger. Until then, script tags are extremely dangerous.

Another use of script tags is to deliver JSON data from different sites. There is absolutely no protection against the case where the different site sends dangerous scripts instead of benign JSON data. Hopefully, some day soon, browsers will offer safe cross-site data transport. Script tags are too dangerous to use for data transport.

Script tags can also be used to deliver Ajax Libraries. Do not load libraries from other sites unless you have a high level of trust in the vendors.

Don’t Send Data To Strangers

If your server sends sensitive data to an evil web page, there is a good chance that the browser will deliver it. In some cases the Same Origin Policy is supposed to block such deliveries, but the fact is that browsers are buggy and sometimes the data will go through. It doesn’t make sense to get mad at the browser. The secure release of confidential information is the server’s responsibility. That responsibility should never be delegated to the browser.

All requests must be authenticated. You must have some secret which is known only by the page that indicates that it should be given the goods. Cookies are not adequate authentication. Don’t use a cookie as the secret. JSON often works best in a dialog: POST a request including the secret in the JSON payload, and get a JSON response in return along with a new secret.

Since script tags are exempt from the Same Origin Policy, a script tag can be used from any page to make a GET request of your server. The request will even include your cookies. If the response contains confidential information, then your server must refuse the request.

There are people who are selling magic wrappers to put around JSON text to prevent delivery to unauthorized receivers. Avoid that stuff. It will fail when new browser bugs are created and discovered, and in some cases might introduce painful new exploits.

Use SSL

Any time you are transmitting confidential information or requests for confidential information, use SSL. It provides link encryption so that your secrets are not revealed in transit.

By Douglas CrockfordApril 10th, 2007
« Older Entries

Pages

  • About
  • Contribute
  • YUI Jobs

Recent Posts

  • 3.11pr1 Released
  • YUI Weekly for June 14th, 2013
  • Pure 0.2.0 Released
  • YUI Weekly for June 7th, 2013
  • YUI 3.10.3 Released to Fix Reintroduced SWF Vulnerability

Archives

Categories

  • Accessibility (25)
  • CSS 101 (8)
  • Design (51)
  • Development (595)
  • Frontend Jobs at Yahoo (13)
  • Graded Browser Support (8)
  • In the Wild (63)
  • Miscellany (11)
  • Open Hours (44)
  • Performance (23)
  • Releases (26)
  • Target Environments (11)
  • Yeti (4)
  • YUI 3 Gallery (29)
  • YUI Events (45)
  • YUI Implementations (55)
  • YUI Theater (146)
  • YUI Weekly (41)

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
© 2013 YUI Blog