Saturday, February 28, 2009

Cross Site Scripting...

Have you ever absolutely needed to do an AJAX call to another domain? Here is a way to do it (non-secure). Basically, we create a new IHttpHandler that listens for a request. It takes a single QueryString parameter, "url". It then passes the request on to the given destination. It supports all HTTP Methods (GET, PUT, HEAD, POST, etc.).

First, create a new class that implements IHttpHandler as follows:

CrossDomainHandler.ashx.cs

using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Web;

namespace CrossDomain
{
 public class CrossDomainHandler : IHttpHandler
 {
  public CrossDomainHandler()
  {
  }

  public void ProcessRequest(HttpContext context)
  {
   try
   {
    string url = context.Request.QueryString["url"];

    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;

    request.Headers.Clear();

    foreach (string key in context.Request.Headers.AllKeys)
    {
     string value = context.Request.Headers[key];

     if (string.Compare(key, "Connection", true, CultureInfo.InvariantCulture) == 0)
     {
      if (string.Compare(value, "Keep-Alive", true, CultureInfo.InvariantCulture) == 0)
      {
       request.KeepAlive = true;
      }
      else if (string.Compare(value, "Close", true, CultureInfo.InvariantCulture) == 0)
      {
       request.KeepAlive = false;
      }
      else
      {
       request.Connection = value;
      }
     }
     else if (string.Compare(key, "Content-Length", true, CultureInfo.InvariantCulture) == 0)
     {
      int length = Convert.ToInt32(value);

      if (length > 0)
      {
       request.ContentLength = length;
      }
     }
     else if (string.Compare(key, "Accept", true, CultureInfo.InvariantCulture) == 0)
     {
      request.Accept = value;
     }
     else if (string.Compare(key, "Content-Type", true, CultureInfo.InvariantCulture) == 0)
     {
      request.ContentType = value;
     }
     else if (string.Compare(key, "Date", true, CultureInfo.InvariantCulture) == 0)
     {
     }
     else if (string.Compare(key, "Expect", true, CultureInfo.InvariantCulture) == 0)
     {
      request.Expect = value;
     }
     else if (string.Compare(key, "Host", true, CultureInfo.InvariantCulture) == 0)
     {
     }
     else if (string.Compare(key, "If-Modified-Since", true, CultureInfo.InvariantCulture) == 0)
     {
      request.IfModifiedSince = DateTime.Parse(value);
     }
     else if (string.Compare(key, "Proxy-Connection", true, CultureInfo.InvariantCulture) == 0)
     {
     }
     else if (string.Compare(key, "Range", true, CultureInfo.InvariantCulture) == 0)
     {
     }
     else if (string.Compare(key, "Referer", true, CultureInfo.InvariantCulture) == 0)
     {
      request.Referer = value;
     }
     else if (string.Compare(key, "Transfer-Encoding", true, CultureInfo.InvariantCulture) == 0)
     {
      request.TransferEncoding = value;
     }
     else if (string.Compare(key, "User-Agent", true, CultureInfo.InvariantCulture) == 0)
     {
      request.UserAgent = value;
     }
     else
     {
      request.Headers.Add(key, value);
     }
    }

    request.Method = context.Request.HttpMethod;

    if (context.Request.ContentLength > 0)
    {
     CopyStream(context.Request.InputStream, request.GetRequestStream(), 512);
    }

    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
    {
     context.Response.ClearHeaders();
     context.Response.ClearContent();

     foreach (string key in response.Headers.AllKeys)
     {
      context.Response.AddHeader(key, response.Headers[key]);
     }

     if (response.ContentLength > 0)
     {
      CopyStream(response.GetResponseStream(), context.Response.OutputStream, 512);
     }
    }
   }
   catch (Exception ex)
   {
    context.Response.Write(ex);
   }
  }

  public bool IsReusable
  {
   get
   {
    return false;
   }
  }

  private static void CopyStream(Stream input, Stream output, int bufferSize)
  {
   byte[] buffer = new byte[bufferSize];
   int count = 0;

   while ((count = input.Read(buffer, 0, buffer.Length)) > 0)
   {
    output.Write(buffer, 0, count);
   }
  }

 }
}

CrossDomainHandler.ashx

<%@ WebHandler Class="CrossDomain.CrossDomainHandler,CrossDomain" %>

Use it as follows:

if (typeof XMLHttpRequest == "undefined") XMLHttpRequest = function() {
  try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}
  try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}
  try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}
  try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
  throw new Error("This browser does not support XMLHttpRequest.")
};

var req = new XMLHttpRequest();

req.open('GET', 'CrossDomainHandler.ashx?url=http%3A%2F%2Fwww.google.com%2F', false);

alert(req.responseText);

Or, extending the Prototype JS Framework:

CrossDomainAjax.js

Ajax.CrossDomainRequest = Class.create(Ajax.Request, {
 initialize: function($super, url, handlerUrl, options) {
  this.handlerUrl = handlerUrl;
  $super(url, options);
 },
 request: function($super, url) {
  this.url = url;
  if (!this.isSameOrigin())
   url = this.handlerUrl + '?url=' + encodeURIComponent(url);
  $super(url);
 }
});

To use:

<script type="text/javascript">
var mine = new Ajax.CrossDomainRequest('http://www.google.com/', 'CrossDomainHandler.ashx', {
 method: 'head',
 onSuccess: function(transport) {
  $('mine').value = transport.getAllResponseHeaders();
 }
});
</script>

Hope this helps…

No comments:

Post a Comment