Web API Security Considerations for ASP.NET Web API

Avoid Revealing Software Version Numbers

Although this information may seem innocuous, it allows an attacker to identify the version of server side components and therefore target further attacks to this specific software. This can quickly lead to successful exploitation if the server side components have vulnerabilities with publicly available exploit code. The server software versions used by the application are revealed by the web server. Displaying version information of software information could allow an attacker to determine which vulnerabilities are present in the software, particularly if an outdated software version is in use with published vulnerabilities.

See the following guide for removing the headers from IIS and ASP.NET: https://blogs.msdn.microsoft.com/varunm/2013/04/23/removeunwanted-http-response-headers

Avoid Information Disclosure via Verbose Error Messages

It is recommended that functionality is implemented within the application that detects when an error has occurred and redirects the user to a custom error page that does not disclose any form of sensitive data. These errors that should induce a redirect to a custom error page should include 403 Forbidden Errors – to prevent an adversary from enumerating existing pages on the application that require prior authentication to view; 404 Not Found pages; and 500 Internal Error pages. Alternatively, simply redirecting a user to the applications home page can also suffice in reducing the level of information disclosed.

For ASP.NET, in web.config set one of the following options: 

<customErrors mode="RemoteOnly" />
<customErrors mode="On" />

Write out Security Headers

A number of HTTP Security Headers have been introduced in recent years to enhance security of a website by providing protections against certain types of attacks. The following table contains the headers which fall under this vulnerability category, notes are offered in the technical analysis as to which headers are missing and any misconfiguration discovered during the engagement.

It’s strongly recommended that these headers are configured on all applications to further harden the application from attack.

Few example headers that should be used:

  • Strict-Transport-Security – HTTP Strict-Transport-Security (HSTS) enforces secure (HTTP over SSL/TLS) connections to the server. This reduces impact of bugs in web applications leaking session data through cookies and external links and defends against Man-in-the-Middle attacks. HSTS also disables the ability for users to ignore SSL negotiation warnings
  • X-Frame-Options – improves the protection of web applications against Clickjacking. It declares a policy communicated from a host to the client browser on whether the browser must not display the transmitted content in frames of other web pages. This functionality can also be achieved with Content-Security-Policy.
  • X-XSS-Protection – enables the Cross-Site Scripting (XSS) filter that is built into most modern web browsers. Typically, the filter is enabled by default; the role of this header is to re-enable the filter if it was disabled by the user and also sets blocking mode which can mitigate some filter bypasses.
  • X-Content-Type-Options – The only defined value, “nosniff”, prevents Internet Explorer and Google Chrome from MIME-sniffing a response away from the declared content-type. This reduces exposure to cross-site scripting vulnerabilities that may not otherwise be present.
  • Content-Security-Policy – requires careful tuning and precise definition of the policy. If enabled, CSP has significant impact on the way browser renders pages (e.g., inline JavaScript disabled by default and must be explicitly allowed in policy). CSP prevents a wide range of attacks, including Cross-Site Scripting and other Cross-Site Injections.
  • Referrer-Policy – governs which referrer information, sent in the Referer header, should be included with requests made.

Additional information, plus examples of all headers to consider can be found at https://securityheaders.com

Use Cross-Site Request Forgery Protection

Cross-Site Request Forgery (CSRF) vulnerabilities arise when applications rely solely on HTTP cookies to identify the user that has issued a particular request. Because browsers automatically add cookies to requests regardless of their origin, it may be possible for an attacker to create a malicious web site that forges a cross-domain request to the vulnerable application. For a request to be vulnerable to CSRF, the following conditions must hold:

  • The request must be issued cross-domain, for example using an HTML form. If the request contains non-standard headers or body content and these are checked server-side, then it may only be issuable from a page that originated on the same domain.
  • The application must rely solely on HTTP cookies or Basic Authentication to identify the user that issued the request. If the application places session-related tokens within the request body, then it may not be vulnerable.
  • The request performs a privileged action within the application based on the identity of the issuing user.
  • The attacker can determine all the parameters required to construct a request that performs the action. If the request contains values that the attacker cannot determine or predict, then it is not vulnerable.

The most effective way to protect against CSRF vulnerabilities is to include within relevant requests an additional token that is not transmitted in a cookie: for example, a parameter in a hidden form field. This additional token should be random enough such that it is not feasible for an attacker to determine or predict the value of any token that was issued to another user. The token should be associated with the user’s session, and the application should validate that the correct token is received before performing any action resulting from the request.

An alternative approach, which may be easier to implement, is to validate that Host and Referer headers in relevant requests are both present and contain the same valid domain name. The Origin header can also be validated against the Host, however some browsers do not support this for requests made from the same domain, therefore this mechanism would need to use the Referer header as a fallback. For AJAX requests, a custom header can be added and then checked server-side (e.g. “X-Requested-With: XMLHttpRequest”), because this cannot be passed cross-domain without CORS being enabled.

However, these header checking approaches are somewhat less robust. Historically, quirks in browsers and implementation errors have often enabled attackers to forge cross-domain requests that manipulate these headers to bypass such defence.

WCF Custom tool error: Failed to generate code for the service reference.

Very recently, building multi-tier application involving Silverlight front-end inter-operating with WCF back-end, I encountered very odd, yet annoying behaviour of Visual Studio 2010. I was building new functionality using Telerik RadMap for Silverlight control that was to display some spartial data supplied by WCF service. To achieve this my Silverlight application had to reference few Telerik assemblies (Telerik.Windows.Controls.DataVisualization.dll). What is important is fact, that Service Reference for WCF service had been there already, project built and everything was working nicely, until I added extra method to WCF. After using “Update Service Reference” VS command, I started getting following error.

WCF error Screenshot no.1

Lack of further details with regards to this error and appropriate feedback from Visual Studio certainly does not help to get to the bottom of the problem, however after some investigation it turned out that one of Telerik assemblies caused this problem. When VS2010 creates service reference, default settings are that it should “reuse” all available types in reference. Excluding certain types from serialization allows service reference to be added successfully.

WCF error screen 2

I hope this helps anybody who got stuck with this problem.

Web service proxy in ASP.NET MVC – how to avoid cross-site script warnings

In this article I will walk through the process of creation of web service proxy in ASP.NET MVC. This is specifically useful as a method of preventing cross-site scripting (XSS) warnings on web page. This is a feature of all modern web browsers and cannot be easily omitted as it enforces same origin policy. This sort of attack can happen when browser tries to download client script from outside of application domain – simply speaking when web browser downloads script from different location.

For the purpose of this demo I will use IP look-up web service available at http://ipinfodb.com/. This web service allows to obtain geodata for given IP address and can be extremely useful for in example centralizing map (i.e. Google maps or Bing maps) on user’s location.

Valid request should look like this: http://ipinfodb.com/ip_query.php?ip=213.180.146.27

Of course it is possible to call this web service at Ajax request level, by simply writing:

    var ipaddress = '213.180.146.27';
    $.get('http://ipinfodb.com/ip_query.php?ip=' + ipaddress + '&timezone=false',
        function(data) {...}
    );

although on all most recent web browsers (IE 8, FF 3.6, Chrome, Safari) user will get very ugly security warning every time browser executes this code. This is due to XSS vulnerability mentioned above and can be extremely annoying to user when application performs this operation on very first page load.

Thankfully ASP.NET provides set of mechanisms to overcome this problem. In simplest words there is an extra layer between web service and Ajax call needed, and this layer is web service proxy.

The core component of proposed solution is a HTTP handler class, in this example named LocationProxyHandler. This class will create and execute HTTP request to web service and return response to client. It needs to implement two interfaces: IHttpHandler and IRouteHandler. First interface guarantees that class can process HTTP request, second allows to use class in routing rule that will be required to redirect requests to the handler class. Implementation of class is as follows:

    public class LocaionProxyHandler : IHttpHandler, IRouteHandler
    {
        public bool IsReusable
        {
            get { return true; }
        }

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

            string str = string.Format(@"http://ipinfodb.com/ip_query.php?ip={0}",
                ipAddress);

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(str);
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream());

            HttpResponse res = context.Response;
            res.ContentType = "application/xml; charcode=utf8";
            res.StatusCode = 200;
            res.Write(reader.ReadToEnd());
        }

        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return this;
        }
    }

The most important method in the class is ProcessRequest(). It takes current HttpContext as parameter and for this context it executes HTTP request to external web service and builds and returns response. Before it fires HTTP request, it takes IP address as parameter from URL.

There is one more thing that needs doing before this proxy can serve the purpose. It has to be registered in ASP.NET MVC routing table within the application. To accomplish this the following code is needed in RegisterRoutes() method inside Global.asax.cs:

    routes.Add(new Route("{action}.proxy", new SocialCitiProxy.LocaionProxyHandler()))

This makes calls like http://localhost:1431/myApp/Service.proxy understandable for MVC engine and ensures the request is redirected to LocationProxyHandler.

Very final step is Ajax request to LocationProxyHandler:

var ipaddress = '213.180.146.27';
$.ajax({
        url: 'GetUserLocation.proxy',
        data: 'ipAddress=' + ipaddress,
        dataType: 'xml',
        contentType: 'application/xml; charset=utf8',
        error: function (xhr, status, e) {...},
        success: function (data) {...}
});

No security warning messages appears now and this is exactly what we wanted to achieve.

Cascading dropdowns with just a bit of Ajax in ASP.NET 2.0

In this sample I am going to go through very common idea of cascading dropdowns. Very basically speaking it is all about having set of dropdowns where next dropdown is populated on the basis of value selected from previous one.

Let’s start with very simple xml document:

<?xml version="1.0" encoding="utf-8" ?>
<People>
    <Person ID="1" Name="Laith Francis" ParentID="0" />
    <Person ID="2" Name="Hammett Swanson" ParentID="0" />
    <Person ID="3" Name="Macon Hayden" ParentID="0" />
    <Person ID="4" Name="Kennan Mooney" ParentID="0" />
    <Person ID="5" Name="Jin Hutchinson" ParentID="4" />
    <Person ID="6" Name="Emery Schwartz" ParentID="1" />
    <Person ID="7" Name="Melvin Garrett" ParentID="2" />
    <Person ID="8" Name="Giacomo Lamb" ParentID="4" />
    <Person ID="9" Name="Stephen Harding" ParentID="3" />
    <Person ID="10" Name="Ian Ward" ParentID="1" />
    <Person ID="11" Name="Raphael Walters" ParentID="2" />
    <Person ID="12" Name="Lee Gould" ParentID="1" />
    <Person ID="13" Name="Carter Carpenter" ParentID="4" />
    <Person ID="14" Name="Kareem Shepard" ParentID="14" />
    <Person ID="15" Name="Zahir Montgomery" ParentID="12" />
    <Person ID="16" Name="Zachery Mcmahon" ParentID="14" />
    <Person ID="17" Name="Hu Dillon" ParentID="12" />
    <Person ID="18" Name="Allistair Bradford" ParentID="13" />
    <Person ID="19" Name="Vincent Bryan" ParentID="11" />
    <Person ID="20" Name="Vance Santos" ParentID="12" />
</People>

Now let’s create very simple .aspx page, that has three dropdowns. First one we will populate using standard ASP.NET binding method so we will use DropDownList control in this instance. Because we do not populate other dropdowns straight away, nothing stops us from using html select tags. The markup can be like this:

<form id="form1" runat="server">
<div>
    Boss:
    <asp:DropDownList DataSourceID="xmlBosses"
        DataTextField="Name"
        DataValueField="ID"
        ID="ddlBosses" runat="server"></asp:DropDownList>
    <asp:XmlDataSource DataFile="~/App_Data/data.xml"
        ID="xmlBosses" runat="server" XPath="/People/Person[@ParentID=0]">
    </asp:XmlDataSource>
    <br />
    Manager:
    <select id="ddlManagers"></select>
    <br />
    Workers:
    <select id="ddlWorkers"></select>
</div>
</form>

Having this markup done we now need a mechanism to populate ddlManagers when ddlBosses changes. Same action needs to happen to ddlWorkers when ddlManagers is changed. To accomplish that we need make Ajax request to some data provider. In our instance, because application is created in ASP.NET 2.0, I opted for web service, (.Net 3.5 with MVC with it’s JsonResult class and Json() method is much more friendly on this front thought).

Firstly we need web service. We want to give it a boss Id and get list of all managers that report to this boss. Web service can look like this one:

public class People : System.Web.Services.WebService {
    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
    public List GetData(int ParentID) { 
        List<int> result = new List<int>(); 

        XmlDocument doc = new XmlDocument(); 
        doc.Load(Server.MapPath("~/App_Data/data.xml"));
        XmlNodeList nodes = doc.SelectNodes("/People/Person[@ParentID=" 
            + ParentID.ToString() + "]");

        foreach (XmlNode node in nodes) {
            Person p = new Person();
            p.ID = Convert.ToInt32(node.Attributes["ID"].Value);
            p.Name = node.Attributes["Name"].Value;
            p.ParentID = Convert.ToInt32(node.Attributes["ParentID"].Value);
            result.Add(p);
        } 
        return result;
    }

    [Serializable]
    public class Person {
       private int _id;
       private string _name;
       private int _parentId;

       public int ID {
           get { return _id; }
           set { _id = value; }
       }

       public string Name {
           get { return _name; }
           set { _name = value; }
       }

       public int ParentID {
           get { return _parentId; }
           set { _parentId = value; }
       }
    }
}

Having all of that we are now ready for very final stage – building Ajax call to execute web service and process returned data. The only thing we need is a bit of jQuery code, that will do it all:

$(function() {
    // for each dropdown bind change event
    $('select').change(function(e) {
        // capture which dropdown caused request
        if(!e) var e = window.event;
        var srcId = e.srcElement.id;

        // create ajax request
        $.ajax({
            url: 'WebServices/People.asmx/GetData', //call to created web method
            data: { ParentID: $(this).val() },  //web method expects ParentID
            contentType: 'application/xml; charset=utf-8',
            dataType: 'xml',
            error: function(xhr, status, e) { ... },
            success: function(data) {
                // if success get dropdown to be updated
                var select = null;
                switch(srcId) {
                    case 'ddlBosses':
                        select = $('select#ddlManagers');
                    break;
                    case 'ddlManagers':
                        select = $('select#ddlWorkers');
                    break;
                    default:                         
                    break;
                };

                // remove items from dropdown
                $(select).empty();

                // loop through Ajax request results
                $('Person', data).each(function() {
                    // for each result create option element
                    var option = document.createElement('option');
                    $(option).text($('Name', this).text()).val($('ID', this).text());
                    // and append it to dropdown
                    $(select).append(option);
                });
            }
        });
    });
});

And this is it!

I hope you will enjoy it!

How to create page with header and iframe covering rest of the page

A few times I needed to have a page where I have an iframe (I know, I know – it is bad design, but sometimes you need to sacrifice!) with header to the top. I tried many different CSS hacks, but could not get it right. Then I came around pageY(), tiny function created by John Resig (jQuery author), which helps to resolve this particular problem brilliantly:

    function ResizeIFrame() {
        var buffer = 40; //scroll bar buffer
        var height = document.documentElement.clientHeight -
            pageY(document.getElementById('ifrm')) + buffer;
        height = (height < 0) ? 0 : height;
        $('iframe#ifrm').css('height', height + 'px');
    }

    function pageY(elem) {
        return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) : 
            elem.offsetTop;
    }

Where ‘ifrm’ is ID of iframe.

The only thing we need now is handing load and resize events:

    window.onload = window.onresize = ResizeIFrame;

This could not be simpler!

Passing list as parameter from AJAX request to MVC controller

I came across this recently when needed to pass array of selected values into MVC controller during Ajax request. Scenario was that on the page I had set of checkboxes and button. User can then make multiple choice selection and submit form using button. Page could look like this:

<ul id="sampleList">
    <li><input type="checkbox" value="1" checked="checked" />text 1</li>
    <li><input type="checkbox" value="2" checked="checked" />test 2</li>
    <li><input type="checkbox" value="3" checked="checked" />test 3</li>
</ul>
<input type="button" id="submitButton" value="submit" />
<span id="spanResults"></span>

Also in the application I have, let’s say, “TestController”. Within this controller I have “GetValue” method, that I want to concatenate input data into a string:

public JsonResult GetValue (List<string> id)
{
return Json(id);
}

Then jQuery code to collect values of checked checkboxes and make Ajaxcall would be as following:

var items = new Array();
$('#sampleList li input:checkbox:checked').each(function() {
    items.push($(this).val());
});
$('#submitButton').click(function() {
    $.ajax({
        type: 'post',
        url: '/Test/GetValues',
        data: { id: items },
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        success: function(data) {
            $('#spanResults').text(data.join(','));
        },
        error: function(e) {
            alert(e.message);
        }
});

This should hopefully put some light on “how on the earth do I pass list as parameters via Ajax to MVC”. Exactly the same mechanics applies to passing complex types and multiple parameters – just wrapp multiple parameters in complext type and you are done!