Posts tagged AJAX

AJAXified Location Search Text Field in 15min Using MS Life Search, Java and EXTJS

A recent project involved a Microsoft Virtual Earth mashup. As it turns out, the initial component set shipped with the Virtual Earth (VE) SDK is rather limited and – quite frankly – very ugly.

One of the things needed was an input field the user can utilize to type in an address or location, which gets then of course shown in the map. Ideally this input field would provide a “suggestion drop down” containing places known to VE. As it turns out, this is accomplished using…

  • Java Servlets for the middle tier (project requirement)
  • EXTJS for the components and the AJAX communication
  • The MS Virtual Earth Life Search web service

The Servlet

Wrapping the call to the VE web service into a servlet has several advantages: We can perform caching of the requests, thus significantly lowering the response time of the service while being able to create nice statistics of what people are searching for. It also allows us to process both the search string and the results on the server to perform e.g. filtering, limiting searches to geographic areas, etc.

Calling the web service to perform the address lookup is very easy:

private static String URL_PARAMETER_PREFIX ;
private static String URL_PARAMETER_POSTFIX;

static{
try {
URL_PARAMETER_PREFIX =  URLEncoder.encode("a", "UTF-8") + "=" + "&" + URLEncoder.encode("b", "UTF-8") + "=";
URL_PARAMETER_POSTFIX = "&" + URLEncoder.encode("c", "UTF-8") + "=" + URLEncoder.encode("0.0", "UTF-8");
URL_PARAMETER_POSTFIX += "&" + URLEncoder.encode("d", "UTF-8") + "=" + URLEncoder.encode("0.0", "UTF-8");
URL_PARAMETER_POSTFIX += "&" + URLEncoder.encode("e", "UTF-8") + "=" + URLEncoder.encode("0.0", "UTF-8");
URL_PARAMETER_POSTFIX += "&" + URLEncoder.encode("f", "UTF-8") + "=" + URLEncoder.encode("0.0", "UTF-8");
URL_PARAMETER_POSTFIX += "&" + URLEncoder.encode("g", "UTF-8")  + "=" ;
URL_PARAMETER_POSTFIX += "&" + URLEncoder.encode("i", "UTF-8")  + "=" ;
URL_PARAMETER_POSTFIX += "&" + URLEncoder.encode("r", "UTF-8") + "=" + URLEncoder.encode("0", "UTF-8");
} catch (UnsupportedEncodingException e) {
URL_PARAMETER_POSTFIX = null;
URL_PARAMETER_PREFIX = null;
e.printStackTrace();
}
}

private String performMSLiveSearch(String address){

if (liveSearchURL == null
|| URL_PARAMETER_PREFIX == null || URL_PARAMETER_POSTFIX == null  ){
// log error here
return "";
}

StringBuilder requestResult = new StringBuilder();

try {
String paramteterString = URL_PARAMETER_PREFIX
+ URLEncoder.encode(address, "UTF-8")
+ URL_PARAMETER_POSTFIX;

// Send request
URLConnection conn = liveSearchURL.openConnection();
conn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(paramteterString);
wr.flush();

// Get the response
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null)
requestResult.append(line);
wr.close();
rd.close();
} catch (Exception e) {
// log exception here
e.printStackTrace();
}

System.out.println(requestResult.toString());

return requestResult.toString();
}

Instead using heavyweight XML parsers, we can extract the needed information fast and simple using regular expressions:

private Pattern ambiguityListPattern = Pattern.compile(".*?new Array\\('(.*?)',.*?\\)");
private Pattern geoLocationPattern = Pattern.compile(".*?AddGeoLocation\\('(.*?)',.*?\\)");

private String[] analyzeSearchResults(String searchResultString){

if (searchResultString == null)
throw new IllegalArgumentException("Parameter must not be null!");

// unfortunately we are stuck with 1.4, so no generics
ArrayList results = new ArrayList();

// extract the "ambiguity list"
Matcher m = ambiguityListPattern.matcher(searchResultString);

while (m.find())
results.add(m.group(1));

//	 extract the "geolocation list"
m = geoLocationPattern.matcher(searchResultString);

while (m.find())
results.add(m.group(1));

return (String[])results.toArray(new String[results.size()]);
}

To finalize the servlet, we implement the doPOST method as follows:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

String searchAddress = req.getParameter("address");
PrintWriter out = resp.getWriter();

// chaching and search string processing goes here
String[] results = analyzeSearchResults(performMSLiveSearch(searchAddress));
LocationSearchCache.getInstance().addSearchResults(searchAddress, results);

// format output for EXTJS array reader
out.print("[");
for (int i=0; i< results.length; i++){
if (i>0)
out.print(",");
out.print("['");
out.print(results[i]);
out.print("']");
}
out.println("]");
out.close();
}

This leaves us with the HTML and Javascript, which – thanks to the great EXTJS library, is a breeze:

 
 

Ajax Virtual Earth Search Test

That’s it! All the magic is done in…

Ext.onReady(function() {

var store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url:SERVLET_ADDRESS, method: 'POST'}),
reader: new Ext.data.ArrayReader({}, [{name: 'address'}])
});

var combo = new Ext.form.ComboBox({
store: store,
queryParam:'address',
displayField:'address',
hideTrigger:true,
typeAhead: false,
triggerAction: 'all',
emptyText:'City, Address or Postal Code',
selectOnFocus:true
});

combo.applyTo('addressCombo');

});

That’s it! We end up with a very flexible component that can be easily plugged into any page, or even modularized into a JSF component.

We used the doGet method of the servlet to spit out statistics about the current state of the query cache, which also only takes a few lines of code.

Security Exploit for for Ajax Based Applications: JavaScript Hijacking

In their recent paper (download here) Brian Chess, Yekaterina Tsipenyuk O’Neil, Jacob West from Fortify Software describe a new type of eavesdropping attack against Ajax-style Web applications called Javascript Hijacking.

The attack is possible because Web browsers don’t protect JavaScript the same way they protect HTML – if a Web application transfers confidential data using messages written in JavaScript, in some cases the messages can be read by an attacker.

The authors analyzed 12 popular Ajax frameworks and show that most of them do nothing to prevent JavaScript hijacking. Some actually require(!) a developer to create a vulnerable server in order to function.

Like so many of these sorts of vulnerabilities, preventing the class of attacks is easy. In many cases, just a few additional lines of code are required. And like so many software security problems, programmers need to understand the security implications of their work so they can mitigate the risks they face. But it seems that is that JavaScript hijacking won’t be solved so easily, because – especially in the web application world – developers often times don’t understand the security implications of their work and thus nothing in their code will prevent the attacks.