- •preface
- •acknowledgments
- •about this book
- •Who should read this book?
- •Roadmap
- •Code conventions
- •Code downloads
- •Author Online
- •About the title
- •About the cover illustration
- •Rethinking the web application
- •A new design for the Web
- •1.1 Why Ajax rich clients?
- •1.1.1 Comparing the user experiences
- •1.1.2 Network latency
- •1.1.3 Asynchronous interactions
- •1.1.4 Sovereign and transient usage patterns
- •1.1.5 Unlearning the Web
- •1.2 The four defining principles of Ajax
- •1.2.1 The browser hosts an application, not content
- •1.2.2 The server delivers data, not content
- •1.2.3 User interaction with the application can be fluid and continuous
- •1.2.4 This is real coding and requires discipline
- •1.3 Ajax rich clients in the real world
- •1.3.1 Surveying the field
- •1.3.2 Google Maps
- •1.4 Alternatives to Ajax
- •1.4.2 Java Web Start and related technologies
- •1.5 Summary
- •1.6 Resources
- •First steps with Ajax
- •2.1 The key elements of Ajax
- •2.2 Orchestrating the user experience with JavaScript
- •2.3 Defining look and feel using CSS
- •2.3.1 CSS selectors
- •2.3.2 CSS style properties
- •2.3.3 A simple CSS example
- •2.4 Organizing the view using the DOM
- •2.4.1 Working with the DOM using JavaScript
- •2.4.2 Finding a DOM node
- •2.4.3 Creating a DOM node
- •2.4.4 Adding styles to your document
- •2.4.5 A shortcut: Using the innerHTML property
- •2.5 Loading data asynchronously using XML technologies
- •2.5.1 IFrames
- •2.5.2 XmlDocument and XMLHttpRequest objects
- •2.5.3 Sending a request to the server
- •2.5.4 Using callback functions to monitor the request
- •2.5.5 The full lifecycle
- •2.6 What sets Ajax apart
- •2.7 Summary
- •2.8 Resources
- •Introducing order to Ajax
- •3.1 Order out of chaos
- •3.1.1 Patterns: creating a common vocabulary
- •3.1.2 Refactoring and Ajax
- •3.1.3 Keeping a sense of proportion
- •3.1.4 Refactoring in action
- •3.2 Some small refactoring case studies
- •3.2.2 Managing event handlers: Observer pattern
- •3.2.3 Reusing user action handlers: Command pattern
- •3.2.4 Keeping only one reference to a resource: Singleton pattern
- •3.3 Model-View-Controller
- •3.4 Web server MVC
- •3.4.1 The Ajax web server tier without patterns
- •3.4.2 Refactoring the domain model
- •3.4.3 Separating content from presentation
- •3.5 Third-party libraries and frameworks
- •3.5.2 Widgets and widget suites
- •3.5.3 Application frameworks
- •3.6 Summary
- •3.7 Resources
- •Core techniques
- •The page as an application
- •4.1 A different kind of MVC
- •4.1.1 Repeating the pattern at different scales
- •4.1.2 Applying MVC in the browser
- •4.2 The View in an Ajax application
- •4.2.1 Keeping the logic out of the View
- •4.2.2 Keeping the View out of the logic
- •4.3 The Controller in an Ajax application
- •4.3.1 Classic JavaScript event handlers
- •4.3.2 The W3C event model
- •4.3.3 Implementing a flexible event model in JavaScript
- •4.4 Models in an Ajax application
- •4.4.1 Using JavaScript to model the business domain
- •4.4.2 Interacting with the server
- •4.5 Generating the View from the Model
- •4.5.1 Reflecting on a JavaScript object
- •4.5.2 Dealing with arrays and objects
- •4.5.3 Adding a Controller
- •4.6 Summary
- •4.7 Resources
- •The role of the server
- •5.1 Working with the server side
- •5.2 Coding the server side
- •5.2.1 Popular implementation languages
- •5.3 The big picture: common server-side designs
- •5.3.1 Naive web server coding without a framework
- •5.3.2 Working with Model2 workflow frameworks
- •5.4 The details: exchanging data
- •5.4.2 Introducing the planet browser example
- •5.5 Writing to the server
- •5.5.1 Using HTML forms
- •5.5.2 Using the XMLHttpRequest object
- •5.5.3 Managing user updates effectively
- •5.6 Summary
- •5.7 Resources
- •Professional Ajax
- •The user experience
- •6.1 Getting it right: building a quality application
- •6.1.1 Responsiveness
- •6.1.2 Robustness
- •6.1.3 Consistency
- •6.1.4 Simplicity
- •6.1.5 Making it work
- •6.2 Keeping the user informed
- •6.2.1 Handling responses to our own requests
- •6.2.2 Handling updates from other users
- •6.3 Designing a notification system for Ajax
- •6.3.1 Modeling notifications
- •6.3.2 Defining user interface requirements
- •6.4 Implementing a notification framework
- •6.4.1 Rendering status bar icons
- •6.4.2 Rendering detailed notifications
- •6.4.3 Putting the pieces together
- •6.5 Using the framework with network requests
- •6.6 Indicating freshness of data
- •6.6.1 Defining a simple highlighting style
- •6.6.2 Highlighting with the Scriptaculous Effects library
- •6.7 Summary
- •6.8 Resources
- •Security and Ajax
- •7.1 JavaScript and browser security
- •7.1.1 Introducing the “server of origin” policy
- •7.1.2 Considerations for Ajax
- •7.1.3 Problems with subdomains
- •7.2 Communicating with remote services
- •7.2.1 Proxying remote services
- •7.2.2 Working with web services
- •7.3 Protecting confidential data
- •7.3.1 The man in the middle
- •7.3.2 Using secure HTTP
- •7.3.3 Encrypting data over plain HTTP using JavaScript
- •7.4 Policing access to Ajax data streams
- •7.4.1 Designing a secure web tier
- •7.4.2 Restricting access to web data
- •7.5 Summary
- •7.6 Resources
- •Performance
- •8.1 What is performance?
- •8.2 JavaScript execution speed
- •8.2.1 Timing your application the hard way
- •8.2.2 Using the Venkman profiler
- •8.2.3 Optimizing execution speed for Ajax
- •8.3 JavaScript memory footprint
- •8.3.1 Avoiding memory leaks
- •8.3.2 Special considerations for Ajax
- •8.4 Designing for performance
- •8.4.1 Measuring memory footprint
- •8.4.2 A simple example
- •8.5 Summary
- •8.6 Resources
- •Ajax by example
- •Dynamic double combo
- •9.1 A double-combo script
- •9.2 The client-side architecture
- •9.2.1 Designing the form
- •9.2.2 Designing the client/server interactions
- •9.3 Implementing the server: VB .NET
- •9.3.1 Defining the XML response format
- •9.4 Presenting the results
- •9.4.1 Navigating the XML document
- •9.4.2 Applying Cascading Style Sheets
- •9.5 Advanced issues
- •9.5.2 Moving from a double combo to a triple combo
- •9.6 Refactoring
- •9.6.1 New and improved net.ContentLoader
- •9.7 Summary
- •Type-ahead suggest
- •10.1 Examining type-ahead applications
- •10.1.2 Google Suggest
- •10.2.1 The server and the database
- •10.3 The client-side framework
- •10.3.1 The HTML
- •10.3.2 The JavaScript
- •10.3.3 Accessing the server
- •10.5 Refactoring
- •10.5.1 Day 1: developing the TextSuggest component game plan
- •10.5.3 Day 3: Ajax enabled
- •10.5.4 Day 4: handling events
- •10.5.6 Refactor debriefing
- •10.6 Summary
- •11.1 The evolving portal
- •11.1.1 The classic portal
- •11.1.2 The rich user interface portal
- •11.2 The Ajax portal architecture using Java
- •11.3 The Ajax login
- •11.3.1 The user table
- •11.4 Implementing DHTML windows
- •11.4.1 The portal windows database
- •11.4.3 Adding the JS external library
- •11.5 Adding Ajax autosave functionality
- •11.5.1 Adapting the library
- •11.5.2 Autosaving the information to the database
- •11.6 Refactoring
- •11.6.1 Defining the constructor
- •11.6.2 Adapting the AjaxWindows.js library
- •11.6.3 Specifying the portal commands
- •11.6.4 Performing the Ajax processing
- •11.6.5 Refactoring debrief
- •11.7 Summary
- •Live search using XSLT
- •12.1 Understanding the search techniques
- •12.1.1 Looking at the classic search
- •12.1.3 Examining a live search with Ajax and XSLT
- •12.1.4 Sending the results back to the client
- •12.2 The client-side code
- •12.2.1 Setting up the client
- •12.2.2 Initiating the process
- •12.3 The server-side code: PHP
- •12.3.1 Building the XML document
- •12.3.2 Building the XSLT document
- •12.4 Combining the XSLT and XML documents
- •12.4.1 Working with Microsoft Internet Explorer
- •12.4.2 Working with Mozilla
- •12.5 Completing the search
- •12.5.1 Applying a Cascading Style Sheet
- •12.5.2 Improving the search
- •12.5.3 Deciding to use XSLT
- •12.5.4 Overcoming the Ajax bookmark pitfall
- •12.6 Refactoring
- •12.6.1 An XSLTHelper
- •12.6.2 A live search component
- •12.6.3 Refactoring debriefing
- •12.7 Summary
- •Building stand-alone applications with Ajax
- •13.1 Reading information from the outside world
- •13.1.1 Discovering XML feeds
- •13.1.2 Examining the RSS structure
- •13.2 Creating the rich user interface
- •13.2.1 The process
- •13.2.3 Compliant CSS formatting
- •13.3 Loading the RSS feeds
- •13.3.1 Global scope
- •13.3.2 Ajax preloading functionality
- •13.4 Adding a rich transition effect
- •13.4.2 Implementing the fading transition
- •13.4.3 Integrating JavaScript timers
- •13.5 Additional functionality
- •13.5.1 Inserting additional feeds
- •13.5.2 Integrating the skipping and pausing functionality
- •13.6 Avoiding the project’s restrictions
- •13.6.1 Overcoming Mozilla’s security restriction
- •13.6.2 Changing the application scope
- •13.7 Refactoring
- •13.7.1 RSS reader Model
- •13.7.2 RSS reader view
- •13.7.3 RSS reader Controller
- •13.7.4 Refactoring debrief
- •13.8 Summary
- •The Ajax craftsperson’s toolkit
- •A.1 Working smarter with the right toolset
- •A.1.1 Acquiring tools that fit
- •A.1.2 Building your own tools
- •A.1.3 Maintaining your toolkit
- •A.2 Editors and IDEs
- •A.2.1 What to look for in a code editor
- •A.2.2 Current offerings
- •A.3 Debuggers
- •A.3.1 Why we use a debugger
- •A.3.2 JavaScript debuggers
- •A.3.3 HTTP debuggers
- •A.3.4 Building your own cross-browser output console
- •A.4 DOM inspectors
- •A.4.1 Using the Mozilla DOM Inspector
- •A.4.2 DOM inspectors for Internet Explorer
- •A.4.3 The Safari DOM Inspector for Mac OS X
- •A.5 Installing Firefox extensions
- •A.6 Resources
- •JavaScript for object-oriented programmers
- •B.1 JavaScript is not Java
- •B.2 Objects in JavaScript
- •B.2.1 Building ad hoc objects
- •B.2.2 Constructor functions, classes, and prototypes
- •B.2.3 Extending built-in classes
- •B.2.4 Inheritance of prototypes
- •B.2.5 Reflecting on JavaScript objects
- •B.2.6 Interfaces and duck typing
- •B.3 Methods and functions
- •B.3.1 Functions as first-class citizens
- •B.3.2 Attaching functions to objects
- •B.3.3 Borrowing functions from other objects
- •B.3.4 Ajax event handling and function contexts
- •B.3.5 Closures in JavaScript
- •B.4 Conclusions
- •B.5 Resources
- •Ajax frameworks and libraries
- •Accesskey Underlining Library
- •ActiveWidgets
- •Ajax JavaServer Faces Framework
- •Ajax JSP Tag Library
- •Ajax.NET
- •AjaxAC
- •AjaxAspects
- •AjaxCaller
- •AjaxFaces
- •BackBase
- •Behaviour
- •Bindows
- •BlueShoes
- •CakePHP
- •CL-Ajax
- •ComfortASP.NET
- •Coolest DHTML Calendar
- •Dojo
- •DWR (Direct Web Remoting)
- •Echo 2
- •FCKEditor
- •Flash JavaScript Integration Kit
- •Google AjaxSLT
- •Guise
- •HTMLHttpRequest
- •Interactive Website Framework
- •Jackbe
- •JPSpan
- •jsolait
- •JSON
- •JSRS (JavaScript Remote Scripting)
- •LibXMLHttpRequest
- •Mochikit
- •netWindows
- •Oddpost
- •OpenRico
- •Pragmatic Objects
- •Prototype
- •Qooxdoo
- •RSLite
- •Ruby on Rails
- •Sack
- •SAJAX
- •Sarissa
- •Scriptaculous
- •SWATO…
- •Tibet
- •TinyMCE
- •TrimPath Templates
- •Walter Zorn’s DHTML Libraries
- •WebORB for .NET
- •WebORB for Java
- •XAJAX
- •x-Desktop
- •XHConn
- •index
- •Symbols
- •Numerics
272CHAPTER 7
Security and Ajax
Under correct game play, both users may set up their pieces in any order and will then call the URL guessPosition.do in turn. The server will police the order of play, returning a “not your turn” response if a player tries to play out of turn.
Let’s now put on our black hats and try to hack the game. We’ve written a client that is able to call the web service API in any order it likes. What can we do to tip the odds in our favor? We can’t give ourselves extra turns because the server monitors that—it’s part of the published API.
One possible cheat is to move a piece after the setup phase is finished. Under the fine-grained architecture, we can try calling positionShip.do while the game is in progress. If the server code has been well written, it will note that this is against the rules and return a negative acknowledgment. However, we have nothing to lose by trying, and it is up to the server-side developer to anticipate these misuses and code defensively around them.
On the other hand, if the server is using the coarse-grained API, it isn’t possible to move individual pieces without also clearing the entire board. Fine-tuning the game in your favor isn’t a possibility.
A coarse-grained API limits the flexibility of any malicious hacker, without compromising the usability for law-abiding users. Under a well-designed server model, use of a fine-grained API shouldn’t present any exploits, but the number of entry points for potential exploits is much higher, and the burden of checking these entry points for security flaws rests firmly with the server tier developer.
In section 5.3.4, we suggested using a Façade to simplify the API exposed by a service-oriented architecture. We recommend doing so again here, from a security standpoint, because a simpler set of entry points from the Internet is easier to police.
Design can limit the exposure of our application to external entities, but we still need to offer some entry points for our legitimate Ajax client to use. In the following section, we examine ways of securing these entry points.
7.4.2Restricting access to web data
In an ideal world, we would like to allow access to the dynamic data served from our app to the Ajax client (and possibly other authorized parties) and prevent anybody else from getting in. With some rich-client technologies, we would have the opportunity of using custom network protocols, but the Ajax application is limited to communicating over HTTP. Secure HTTP can keep the data in individual transactions away from prying eyes, as we discussed earlier, but it can’t be used to determine who gets to call a particular URL.
Policing access to Ajax data streams |
273 |
|
|
Fortunately, HTTP is quite a rich protocol, and the XMLHttpRequest object gives us a good level of fine-grained control over it. When a request arrives on the server, we have access to a range of HTTP headers from which we can infer things about the origin of the request.
Filtering HTTP requests
For the sake of providing concrete examples, we’ll use Java code here. Other server-side technologies offer similar ways to implement the techniques that we are describing, too. In the Java web application specification, we can define objects of type javax.servlet.Filter, which intercept specific requests before they are processed at their destination. Subclasses of Filter override the doFilter() method and may inspect the HTTP request before deciding whether to let it through or forward it on to a different destination. Listing 7.5 shows the code for a simple security filter that will inspect a request and then either let it through or forward it to an error page.
Listing 7.5 A generic Java security filter
public abstract class GenericSecurityFilter implements Filter { protected String rejectUrl=null;
public void init(FilterConfig config) throws ServletException {
rejectUrl=config.getInitParameter("rejectUrl"); b
}
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, |
ServletException { |
|
|
|
c Check request validity |
if (isValidRequest(request)){ |
|
|
|
||
|
|
|
|||
chain.doFilter(request, response); |
|
d Forward to reject URL |
|||
}else if (rejectUrl!=null){ |
|
||||
|
|||||
RequestDispatcher |
dispatcher |
|
|
|
|
=request.getRequestDispatcher(rejectUrl); dispatcher.forward(request, response);
}
}
protected abstract boolean isValidRequest(ServletRequest request);
public void destroy(){}
}
274CHAPTER 7
Security and Ajax
The filter is an abstract class, defining an abstract method isValidRequest() that inspects the incoming request object before passing a verdict. If the method fails c, it is forwarded to a different URL d, which is defined in the configuration file for the web application b, which we’ll look at shortly.
This filter provides us with considerable flexibility in defining a concrete subclass. We can adapt it to more than one security strategy.
Using the HTTP session
One common approach is to create a token in the user’s HTTP session when she logs in and check for the existence of that object in session during subsequent requests before performing any other actions. Listing 7.6 demonstrates a simple filter of this type.
Listing 7.6 Session token-checking filter
public class SessionTokenSecurityFilter extends GenericSecurityFilter {
protected boolean isValidRequest(ServletRequest request) { boolean valid=false;
HttpSession session=request.getSession(); if (session!=null){
UserToken token=(Token) session.getAttribute('userToken'); if (token!=null){
valid=true;
}
}
return valid;
}
}
This technique is commonly used in conventional web applications, typically forwarding to a login screen if validation fails. In an Ajax application, we are free to return a much simpler response in XML, JSON, or plain text, which the client could respond to by prompting the user to log in again. In chapter 11, we discuss a fuller implementation of such a login screen for our Ajax Portal application.
Using encrypted HTTP headers
Another common strategy for validating a request is to add an additional header to the HTTP request and check for its presence in the filter. Listing 7.7 shows a second example filter that looks for a specific header and checks the encrypted value against a known key held on the server.
Policing access to Ajax data streams |
275 |
|
|
Listing 7.7 HTTP header-checking filter
public class SecretHeaderSecurityFilter extends GenericSecurityFilter {
private String headerName=null;
public void init(FilterConfig config) throws ServletException { super.init(config); headerName=config.getInitParameter("headerName");
}
protected boolean isValidRequest(ServletRequest request) { boolean valid=true;
HttpServletRequest hrequest=(HttpServletRequest)request; if (headerName!=null){
valid=false;
String headerVal=hrequest.getHeader(headerName); b Encrypter crypt=EncryptUtils.retrieve(hrequest);
if (crypt!=null){
valid=crypt.compare(headerVal); c Compare header value
}
}
return valid;
}
}
When testing the request, this filter reads a specific header name b and compares it with an encrypted value stored in the server session c. This value is transient and may be generated randomly for each particular session in order to make the system harder to crack. The Encrypter class uses the Apache Commons
Codec classes and javax.security.MessageDigest classes to generate a hexencoded MD5 value. The full class listing is available in the downloadable code that accompanies this book. The principle of deriving a hex-encoded MD5 in Java is shown here:
MessageDigest digest=MessageDigest.getInstance("MD5"); byte[] data=privKey.getBytes();
digest.update(data);
byte[] raw=digest.digest(pubKey.getBytes()); byte[] b64=Base64.encodeBase64(raw);
return new String(b64);
where privKey and pubKey are the private and public keys, respectively. To configure this filter to review all URLs under the path /Ajax/data, we can add the following filter definition to the web.xml configuration file for our web application:
276CHAPTER 7
Security and Ajax
<filter id='securityFilter_1'> <filter-name>HeaderChecker</filter-name> <filter-class>
com.manning.ajaxinaction.web.SecretHeaderSecurityFilter </filter-class>
<init-param id='securityFilter_1_param_1'> <param-name>rejectUrl</param-name> <param-value>/error/reject.do</param-value>
</init-param>
<init-param id='securityFilter_1_param_2'> <param-name>headerName</param-name> <param-value>secret-password</param-value>
</init-param> </filter>
This configures the filter to forward rejected requests to the URL /error/reject.do, after checking the value of HTTP header “secret-password.” To complete the configuration, we define a filter mapping to match this filter to everything under a specific path:
<filter-mapping> <filter-name>HeaderChecker</filter-name> <url-pattern>/ajax/data/*</url-pattern>
</filter-mapping>
On the client side, the client can generate Base64 MD5 digests using Paul Johnston’s libraries (which we discussed earlier in this chapter). To add the required HTTP header on our Ajax client, we use the setRequestHeader() method, as outlined here:
function loadXml(url){ var req=null;
if (window.XMLHttpRequest){ req=new XMLHttpRequest();
} else if (window.ActiveXObject){
req=new ActiveXObject("Microsoft.XMLHTTP");
}
if (req){ req.onreadystatechange=onReadyState; req.open('GET',url,true);
req.setRequestHeader('secret-password',getEncryptedKey()); req.send(params);
}
}
where the encryption function is simply defined as the Base64 MD5 digest of a given string: