Wednesday 30 September 2009

Enabling private cross site scripting with a XMLHttpRequest proxy servlet

The problem

You have a list of URLs and want to interrogate them for their Content-Type using javascript in the browser.

You cannot do this because the javascript security model forbids this activity, called cross site scripting, as it could be used to invoke malign code.

The solution

Install a proxy on your server which can make the request on your behalf and relay it back to you. You have not violated the javascript security model and you are free to invoke any url on the net by a call to your own, originating, server.

However the proxy does need to be protected otherwise it could be abused by other sites. To protect the proxy we ensure that it can only be accessed from a page generated by our server, indeed one which has set a named session variable.

The code As implemented within the Melati framework, which wraps the HttpRequest and HttpResponse objects in a Melati object:
private String proxy(Melati melati, ServletTemplateContext context) {
  if (melati.getSession().getAttribute("generatedByMelatiClass") == null)
    throw new AnticipatedException("Only available from within an Admin generated page");
  String method = melati.getRequest().getMethod();
  String url =  melati.getRequest().getQueryString();
  HttpServletResponse response = melati.getResponse();
  HttpMethod httpMethod = null;
  try {

    HttpClient client = new HttpClient();
    if (method.equals("GET"))
      httpMethod = new GetMethod(url);
    else if (method.equals("POST"))
      httpMethod = new PostMethod(url);
    else if (method.equals("PUT"))
      httpMethod = new PutMethod(url);
    else if (method.equals("HEAD"))
      httpMethod = new HeadMethod(url);
    else
      throw new RuntimeException("Unexpected method '" + method + "'");
    try {
      httpMethod.setFollowRedirects(true);
      client.executeMethod(httpMethod);
      for (Header h : httpMethod.getResponseHeaders()) {
        response.setHeader(h.getName(), h.getValue());
      }
      response.setStatus(httpMethod.getStatusCode());
      response.setHeader("Cache-Control", "no-cache");
      byte[] outputBytes = httpMethod.getResponseBody();
      if (outputBytes != null) {
        response.setBufferSize(outputBytes.length);
        response.getWriter().write(new String(outputBytes));
        response.getWriter().flush();
      }
    } catch (Exception e) {
      throw new MelatiIOException(e);
    }
  } finally {
    httpMethod.releaseConnection();
  }
  return null;
}

No comments:

Post a Comment