locked
Validate callBack parameter value for JSONP request RRS feed

  • Question

  • We are using the crossDomainScriptAccessEnabled tag in web.config to support JSONP from our WCF service. Now one of the security tools we ran has a recommendation to validate the callback parameter value and reject any value which is not alphanumeric.  Now since this callback parameter is
    added internally by .NET framework,  how can we validate the value of this parameter ?


    Tuesday, February 21, 2012 12:39 AM

Answers

  • One easy way is to use a message inspector at the server side. For all incoming GET messages with a "callback" query string parameter, you'd retrieve the value of that parameter, then use some regex to validate that it only contains alphanumeric characters, throwing an exception otherwise. The code below shows how this can be done.

        public class Post_334b9b23_950c_4b58_b120_b19dc9478ad5
        {
            [ServiceContract]
            public class Service
            {
                [WebGet]
                public int Add(int x, int y)
                {
                    return x + y;
                }
    
                [WebGet]
                public int Subtract(int x, int y)
                {
                    return x - y;
                }
            }
            public class MyInspector : IEndpointBehavior, IDispatchMessageInspector
            {
                public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
                {
                }
    
                public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
                {
                }
    
                public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
                {
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
                }
    
                public void Validate(ServiceEndpoint endpoint)
                {
                }
    
                public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
                {
                    HttpRequestMessageProperty prop;
                    prop = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
                    if (prop != null)
                    {
                        if (prop.Method == "GET")
                        {
                            NameValueCollection nvc = HttpUtility.ParseQueryString(prop.QueryString);
                            string callback = nvc["callback"];
                            if (!string.IsNullOrEmpty(callback))
                            {
                                Regex letterNumberOnly = new Regex(@"^[0-9a-zA-Z]+$");
                                if (!letterNumberOnly.IsMatch(callback))
                                {
                                    throw new Exception("This will abort the request, causing a 400 to be returned to the client");
                                }
                            }
                        }
                    }
    
                    return null;
                }
    
                public void BeforeSendReply(ref Message reply, object correlationState)
                {
                }
            }
            public static void Test()
            {
                string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
                ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
                WebHttpBinding binding = new WebHttpBinding();
                binding.CrossDomainScriptAccessEnabled = true;
                ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(Service), binding, "");
                WebHttpBehavior behavior = new WebHttpBehavior();
                behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
                endpoint.Behaviors.Add(behavior);
                endpoint.Behaviors.Add(new MyInspector());
                host.Open();
                Console.WriteLine("Host opened");
    
                WebClient c;
    
                c = new WebClient();
                Console.WriteLine("No callback:");
                Console.WriteLine(c.DownloadString(baseAddress + "/Add?x=6&y=8"));
                Console.WriteLine();
    
                c = new WebClient();
                Console.WriteLine("Valid callback:");
                Console.WriteLine(c.DownloadString(baseAddress + "/Add?x=6&y=8&callback=Func"));
                Console.WriteLine();
    
                c = new WebClient();
                try
                {
                    Console.WriteLine("Invalid callback:");
                    Console.WriteLine(c.DownloadString(baseAddress + "/Add?x=6&y=8&callback=Fu-nc"));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
    
                Console.WriteLine();
    
                Console.Write("Press ENTER to close the host");
                Console.ReadLine();
                host.Close();
            }
        }
    


    Carlos Figueira

    Tuesday, February 21, 2012 12:56 AM