Writing secure ASP.NET code – Part 1

This is the first part of multi-part series on writing a secure ASP.NET code. It was inspired by the online training class Creating Secure Code for ASP.Net offered by TeamProfessor from Security Innovations.
Software security has three principles:

  1. Integrity – Only authorized parties are allowed to modify stored and transmitted information.
  2. Confidentiality – Only authorized parties are allowed to view the information.
  3. Availability – The information is available anytime needed.

Before we can talk about software bugs and defects, we need understand the distinction between them. Software bugs are flaws uncovered during development phase, before the application goes into production. Software defects are flaws uncovered after delivery of a given system to its end-users.
Defects can be classified into two categories:

  • Traditional – The intended application functionality is missing or it was improperly implemented.
  • Security – Extra and unwanted capabilities that may allow malicious users to attack application.

The cost of fixing defects increases exponentially over time. It is imperative to fix them in the phase they were introduced. Applying secure coding best practices can prevent a large number of implementation-level security vulnerabilities reaching the final version of the software. Code reviews performed by another engineer or a tool should be used to verify that secure coding best practices have been followed.
Performing input and data validation
Common input vulnerabilities:

  1. SQL injection – Allows attacker to perform any database operation including executing a shell commands on the server using xp_cmdshell stored procedure. If we have a dynamically generated query:
    SELECT FirstName FROM Users WHERE LastName=’” + lastname + “’”;

    The attacker can enter the following text in the lastname text box on the web page:

    ‘SELECT Password FROM Users WHERE LastName = ‘Smith

    This input would allow the attacker to retrieve the password for person with the last name Smith.

  2. HTTP Response splitting –Web application taking user input and coping it into the header of the response without performing validation. Most common type of HTTP response splitting attack is cross-site scripting (XSS). XSS is injecting a malicious script code into the page. It can be categorized as:
    • Reflective – Web application reflects part of an HTTP request back to the user without first sanitizing it. This occurs when malicious code is included as a GET or POST parameter.
    • Persistent – Web application stores user generated data and then later displays this data back to the user. It is common for social networking sites, wikis, forums, etc.
    • DOM-based – Affects applications that perform client-side processing of user input using JavaScript or VBScript. Common for applications that dynamically generate HTML content without sanitizing the user input.

Input validation and output encoding is necessary to protect from these attacks. A white-list approach should be used when possible. The validation should be implemented on the server side because client validation can be easily bypassed by turning off JavaScript in the browser. Client side validation may be used in order to improve responsiveness of the application and make user experience more pleasant. It should not be used as a security measure. The input needs to be validated for its length, type, and format. ASP.NET offers several server side validator controls that make it easy for a developer to validate user input. For numeric range enforcement, the RangeValidator control is used:

<td>
  <asp:RangeValidator id="Range1"
           ControlToValidate="txtState"
           MinimumValue="1"
           MaximumValue="50"
           Type="Integer"
           EnableClientScript="false"
           Text="The value must be from 1 to 10!"
           runat="server"> *
  </asp:RangeValidator>
</td>
<td>State:</td>
<td><input type=text runat=server id=txtState></td>

CompareValidator is suitable for cases where two inputs need to be compared. A common case would a password field:

<asp:CompareValidator runat=server
            ControlToValidate=txtRePWord
            ControlToCompare=txtPWord
            ErrorMessage="Passwords do not match." />

When it was needed to ensure that input contains specific format such as a zip code or a phone number then RegularExpressionValidator is the best choice:

<input type=text runat=server id=txtZip>
<asp:RegularExpressionValidator runat=server
ControlToValidate="txtZip"
ErrorMessage="Zip code must be 5 digits, all
numbers."
ValidationExpression="[0-9]{5}"> *
</asp:RegularExpressionValidator>
<td>Zip code:</td>
<td><input type=text runat=server id=txtZip></td>

If none of the controls satisfy the business requirements, then CustomValidator may be used. The validation logic is implemented in a method that is invoked by the control:

protected void CheckID(object source, ServerValidateEventArgs args)
{
  args.IsValid = !args.Value.ToLower().StartsWith("a");
}
<asp:customvalidator controltovalidate="txtName" errormessage="ID is already in use." onservervalidate="CheckID" runat="server">

In some cases validators cannot be used. The input comes from files, query string, or some other source. In that case the recommended approach is to use Regex class:

using System.Text.RegularExpressions ;
// Instance method:
Regex reg = new Regex(@"^[a-zA-Z'.\s]{1,40}$");
Response.Write(reg.IsMatch(Request.QueryString["Name"]));

// Static method:
if (!Regex.IsMatch(Request.QueryString["Name"],@"^[a-zA-Z'.\s]{1,40}$")) 
{
// Name does not match expression
}

The regular expressions that are frequently used should be cached. If that is not possible then the static IsMatch method should be used for performance reasons, to avoid unnecessary object creation.

Any input, no matter what its source is, a web page, query string, cookie, database, file, hidden-field, HTTP header must not be trusted. It should be validated on the server using the most appropriate method. Using a centralized set of validators reduces potential flaws, makes them more maintainable, provides consistent user experience. If the data is being echoed back then it needs to be encoded just before returning it back. It is a common practice to use ASP.NET Server.HtmlEncode fuction. This function only encodes <>”& characters. The following script would be vulnerable:

<img id='img<%=Server.HtmlEncode(Request.QueryString["userId"])%>'
src='/image.gif' />

A client side script can be injected by setting userId to:

' onload=alert('xss') alt='

Alternative recommended white-list approach is to use Miscrosoft’s AntiXSSLibrary for output encoding. For HTML encoding use Microsoft.Security.Application.AntiXSSLibrary.HtmlEncode and for URL encoding use Microsoft.Security.Application.AntiXSSLibrary.UrlEncode.
The only approach that prevents all XSS attacks is encoding all non-alphanumeric characters. It is easily accomplished using Regex class:

public static string HtmlEncode(string x)
{
  if (x == null)
  {
    return x;
  }
  return Regex.Replace(x, "[^a-zA-Z0-9]+", new MatchEvaluator(WebUtility.EncodeMatch));
}

Next week
I will continue to expand on security in ASP.NET by looking at encryption, how to securely handle application errors, and some other interesting topics related to ASP.NET security.

Update – 2/21/2010
A straightforward simple example of XSS attack prevention.
A regular expression tutorial with examples of common expressions such as email address, phone number, zip code, etc.

Leave a Reply

Your email address will not be published. Required fields are marked *