Writing secure ASP.NET code – Part 5

This is the fifth and the last part of the multi-part series on writing secure ASP.NET code. The post series was inspired by the online training class Creating Secure Code for ASP.NET offered by TeamProfessor from Security Innovations. In the previous posts, I have discussed input validation, types of online attacks, secure error handling, best practices for storing sensitive data, securing web application by proper configuration, and user account management and secure logging. Today, I am going to describe how to implement proper authorization
Authorization implementation
Performing authorization on the client is insufficient. Authorization needs to be performed on the server and any data re-submitted to the server needs to be re-validated.
ASP.NET provides tools such as ASP.NET role and membership providers. They allow software developers to create web sites, which allow users to create user accounts and have roles assigned.
Leverage URL authorization. The rules can be specified in the web.config:

<authorization>
  <allow users="John"/>
  <deny users="*"/>
</authorization>

Cross site forgery attack is a common type of an attack. It happens when session ID is stolen. It is prevented by assigning a unique ID to each web page form. The ID is checked on a form submission. ASP.NET has built-in protection mechanism, ViewStateUserKey. It is stored in the ViewState and compared with the one stored in the form. ViewStateUserKey property can be set for individual page:

void Page_Init (object sender, EventArgs e)
{
    ViewStateUserKey = Session.SessionID;
 } 

To do this for all pages of your application add following code to the page derived class:

protected override OnInit(EventArgs e)
{
    base.OnInit(e);
    ViewStateUserKey = Session.SessionID;
}

Defending against information disclosure
An attacker may try to gain access system specific data. Therefore, it is important to handle debugging and error handling information securely. Debugging should be turned off in production environment in web.config:

<configuration>
          <system.Web>
              <compilation debug="false">
          </system.Web>
</configuration>

Allow trace info to be accessed only from the local server. Use the trace element in the application’s web.config:

<trace enabled="true" pageOutput="false" traceMode="SortByTime" localOnly="true"/>

Maintain consistency in displayed error messages. Displaying different error messages tells the attacker that different application path has been taken. This may provide him with inner workings of the application and allow his to compromise the system. All error messages should be defined in a centralized resource file for consistency and maintenance reasons.
Defense in depth
Defense in depth refers to multiple layers of defense. It is necessary to have more than one defense mechanism in case the attacker manages to circumvent the first security barrier.
Mechanisms that follow black-list approach are susceptible to bypasses.
Using direct object references provides too much information that can be used to orchestrate an attack. Using indirect object references such as index to provide access to the files, records, directories instead of direct references is preferred approach.
ASP.NET authorization configured in web.config can be circumvented. In order to protect your application from the attack fine tune access specific web page:

[System.Security.Permissions.PrincipalPermission
(System.Security.Permissions.SecurityAction.Demand,Role=@"AuthorizedUsers")]
public class ProtectedPage: System.Web.UI.Page
{
}

In the above example, using class annotations means that only members of the “AuthorizedUsers” group can access pages of type ProtectedPage. Everyone else, including unauthenticated users, will trigger an exception when accessing these pages, and will be redirected to a generic error page.
Encrypting data in the database is the last line of defense. The SQL server installation creates a symmetric master key that is used for encryption of all keys on the server. It is the root encryption object for all certificates, data, keys. A master key is created:

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'WeZ#6hv*XHq#akAEaqcr7%CUP3aQ'

The first storage location is in the sys.symmetric_keys database table, where it is encrypted by the supplied password, and the second is the sys.databases table in the master database, where it is encrypted using the Service Master Key.
Encrypting data in the database can be done in the SQL server. Lets assume we have a following table:

CREATE TABLE Customer ( CustId int, name nvarchar(30), 
City varchar(20), CreditCardType varbinary(300),
CreditCardNumber varbinary(300),
Notes varbinary(4000))
GO

Encrypted data is always a binary blob, no matter what the input type, so the table must use varbinary fields to hold it. We can add encrypted data to the above defined table:

OPEN ASYMMETRIC KEY User1AsymmetricKey
USING PASSWORD = 'AGreatPassword'
INSERT INTO Customer VALUES (5, 'Sally Roe', 'Chatinika',
EncryptByKey(Key_GUID('User1AsymmetricKey'), 'Visa'),
EncryptByKey(Key_GUID('User1AsymmetricKey'), '1234-5678-9009-8765'),
EncryptByKey(Key_GUID('User1AsymmetricKey'), 'One of our best customers. Treat like royalty.'))
CLOSE ASYMMETRIC KEY User1AsymmetricKey

Conclusion
This article completes the marathon series that ended up being much longer than I originally envisioned. Even though there were 5 articles in the series, they barely scratched the surface of secure programming using ASP.NET.
Next week
We are moving to a new topic next week. It is a really world problem I encountered recently. It deals with application integration with a third party webservice. The trick was that the webservice was not reachable during development. I will demonstrate how to reverse engineer a webservice from a wsdl file.

Leave a Reply

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