Wednesday, February 10, 2010

Reducing Information Disclosure in ASP.NET Web Services

*Note: This article does not directly apply to WCF Data Services

Many applications use external web services to allow partners, WPF/Silverlight applications, cloud components, or other entities to access information and functionality.  Trusted parties can use SOAP, REST, or AJAX requests to communicate with ASP.NET Web Service end-points.

Since standard protocols are used to connect with these components, malicious users can also issue requests to your web services.  In some cases, web services provide detailed information regarding the method calls and parameters available, as well as detailed error messages for failed attacks.  It's important to reduce the amount of information provided to attackers by ASP.NET web services.

Reference Web Service
First, let's start with a basic ASP.NET Web Service.  The code is provided below.


This web service accepts a dividend and a divisor and it returns the quotient.  Since this is a simple demonstration, the code does not include any functionality or data that an attacker would likely target, but the concepts that are demonstrated still apply.

If we run this application and visit the Math.asmx page, a description page is displayed.  The description page lists all the web service methods, parameters, and even provides example SOAP requests for calling the methods.  Since I am accessing the service locally, it also provides an HTML form to test the functionality.  Depending on the settings in the Web.Config file, this form may or may not be available to external users.



In addition to the description page, the WSDL document is also accessible.


A client can call this service using a SOAP or REST request, as shown below.



By default, the application displays detailed error messages.  Two example exceptions are shown below.  The first exception is due to a divide by zero condition; the second is due to missing parameter values in the SOAP request.



ASP.NET Custom Errors: 80% Effective

ASP.NET applications have a Custom Error Handler that can be used to control the detail level of error messages provided to clients.  It is very easy to configure the Custom Error Handler in the Web.Config file.  See the "customErrors" XML element in the screenshot below.


After enabling Custom Error Handling, the error messages for the divide by zero condition shows much less detail. We have eliminated the stack trace information; however the title of the error is still present. In a real world web service, SQL Exceptions such as "Unclosed Quotation Mark" would still be shown. Simply enabling Custom Errors is not enough to resolve this information disclosure issue.


Explicit Try/Catch Blocks: 100% Effective, But What If You Miss One
It's considered a best practice to catch and correctly handle any exceptions that occur within code.  This technique is also important for preventing detailed error messages from reaching the client.  Consider the code below.


In this sample, we catch the divide by zero exception and then just return 0.  In a real world application, a much more robust solution should probably be implemented.  This new code results in no business functionality related exception being shown the user.


While this solution is effective at preventing verbose error messages from reaching the client, there are cases when a try/catch block could be missed.  One could wrap all code in a try/catch block for the generic Exception class, but this is not a very elegant solution.

Suppress Returning Exceptions: A Great Backup to Try/Catch Blocks
Wouldn't it be nice if there was a "customErrors" style solution for web services? The "diagnostics" XML element within the web services section of the ASP.NET Web.Config file can provide this type of functionality.


This configuration change results in the following limited error message for the divide by zero condition.


This solution is very effective; however there is one caveat.  Both explicit try/catch blocks and the Web.Config change still will not control error conditions that occur due to missing parameters or incorrect value types.


This solution provides a great catch all for situations where an explicit try/catch block may be missed.

Removing the WSDL and Service Description Pages
A quick Web.Config change can be used to disable WSDLs and description pages.
Once this change has been made, it will be necessary to communicate WSDLs or web service signatures with partners through some other channel.

2 comments:

Wyatt said...

You could avoid all of this by only allowing trusted users to access your REST endpoints. Also, if you are allowing trusted devs to access your REST endpoints you would want to provide them with some of these exception messages so that they can debug what is failing.

Nick Coblentz said...

Wyatt,

You are correct for some situations; however, it will not always workout that only trusted folks will access the end-points.

That said, this article provides as many methods as possible to reduce information disclosure in your web services. In an actual business application, the need to make the service secure must be balanced against the business requirements of its partners or consumers.