Web applications are subject to several types of attacks, whose damage and impact can vary quite a bit depending on the characteristics of the application itself. As a result, security is strictly related to the application’s usage and how the users interact with its features. But how do you design and code secure ASP.NET applications?
From an application point of view, security is mostly a matter of authenticating users and authorizing actions on the system’s resources. ASP.NET provides a range of authentication and authorization mechanisms implemented in conjunction with IIS, the .NET Framework, and the underlying security services of the operating system.
When a client issues a Web request, the following sequence of authentication and authorization events occurs.
- IIS authentication
- ASP.NET authentication
- ASP.NET authorization
If the page can be viewed, and the request comes from a nonrestricted IP address, IIS authenticates the caller using any of the predefined authentication mechanisms. IIS first ensures that the request comes from a trusted IP address. If not, the request is rejected with HTTP error 403.6. A second preliminary check is then made to determine whether the requested resource is available for reading or browsing. If not, the request is rejected with HTTP error 403.2. Next, IIS attempts to authenticate the caller using the Integrated, Digest, or Basic authentication method. If the Windows Integrated method is used, either Kerberos or NTLM is used. If the request passes this stage, ASP.NET gets involved.
ASP.NET supports three types of authentication methods: Windows, Forms, and Passport. If ASP.NET is configured for Windows authentication, no additional steps are needed and ASP.NET just accepts any security token it receives from IIS. If ASP.NET is configured for Forms authentication, the user is prompted for credentials using an HTML form. The User ID and password are authenticated against a stored list of valid users. The application is free to choose the best-suited storage mechanism, including a SQL Server database or Active Directory services. Finally, if ASP.NET is configured for Passport authentication, the user is redirected to a Passport Web site and authenticated by the Passport service.
A fourth type of authentication is None, meaning that ASP.NET does not attempt to perform its own authentication and completely relies on the authentication already carried out by IIS. In this case, anonymous users can connect, and resources are accessed using the ASP.NET account. Setting the ASP.NET authentication mode to the None option does not prevent the application from implementing its own personal authentication layer.
You choose the ASP.NET authentication mechanism using the <authentication> section in the Web.config file. By default, the authentication mode is set to Windows.
Of course, authentication means only that the user is known and proven to be who he or she claimed to be. The next task is to make sure the user has enough rights to access the requested resource.
After authentication, ASP.NET verifies that the caller is authorized to access the requested resource to execute the operation. A couple of HTTP modules provide for this service: UrlAuthorizationModule and FileAuthorizationModule. The former ensures that the authorization rules set in the <authorization> element of the Web.config file are fulfilled. The latter gets into the game when the Windows authentication is used and checks that the caller has the necessary permission to access the requested resource. In this case, the verification is performed comparing the access control list (ACL) of the resource against the caller’s token. At this stage of the process, .NET roles can also be used to verify the caller’s authorization to work on a resource.
The authorization rules consist of two distinct blocks of information regarding what is allowed and what is denied. Under the <authorization> element, the child tag <allow> defines users, roles, and actions allowed; conversely, the child tag <deny> indicates which users, roles, or actions are not permitted.
You should note that the authentication mode can be set only in the machine.config file or, better yet, in the application-level Web.config file. Child subdirectories inherit the authentication mode chosen for the application. However, authorization settings can be defined in the Web.config of each child subdirectory. In other words, authorization supports a finer granularity than authentication.
If you need to authenticate users, chances are good that you also need to serve them personalized pages. There are basically two possibilities here: You either implement a profile system and store configuration information for each user or you define roles and map users to one or more of these categories.
In the former case, you maintain profile records that probably need to store UI-related settings items and references to functions to enable or disable. While designing the page, you access the profile record for the current user and develop the page accordingly.
If you don’t have to maintain user-specific information but simply need to catalog groups of users and maintain profile information on a per-group basis, roles are a better approach. A role is a name—just a short descriptive string—that identifies a set of functions, user interface elements, and permissions that the page grants to each user who plays that role.
Defining roles is a two-step procedure. First, you define all the possible roles and give each user one or more of them. This association is normally done at the database level. Typically, you run your database of users in which you store user names and passwords; add a third column with the role of each.
By the time a user is authenticated, no role information is associated with the identity. However, a function to check whether a given identity plays a certain role exists. You use the IsInRole function of Page.User object to check the role of a user:
Response.Write("The user is the boss");
The second part of defining roles is to associate a role with an authenticated user. To do this, you must create a new principal object, either generic or of the same type of the authentication. This is normally done in the Global.asax file while handling the AuthenticateRequest event:
// role is the string read from the database for the current user
Context.User = new GenericPrincipal(User.Identity, role);
At this point, the authentication module can check the role of the user against the <authorization> section of the local Web.config file. For example, a folder whose Web.config file contains the following script would make its pages accessible only to users belonging to the Boss role:
<allow roles="Boss" />
<deny users="*" />
The authentication module uses the IsInRole function to authorize access by role.
Protect your application
Securing a Web application entails protecting the Web server against a variety of attacks, but it also requires implementing effective policies to prevent illicit accesses to pages. ASP.NET provides some built-in layers of code to authenticate and authorize users and actions.