ASP.NET Security Design Guidelines

Beyond Credentials: Component Design and Deployment Decisions

12 Min Read
ITPro Today logo in a gray background | ITPro Today

ASP.NET Under the Hood

LANGUAGES:VB.NET | C#

ASP.NETVERSIONS: 2.0

 

ASP.NET Security Design Guidelines

Beyond Credentials: Component Design and DeploymentDecisions

 

By Michele Leroux Bustamante

 

Welcome back ASP.NET architects and developers! This monthwe tackle component design and deployment. As always, samples are provided inC# and VB.NET (see end of article for download details). Please send yourquestions and requests to [email protected] let me unravel some ASP.NET mysteries for you!

 

Q. Could you write a summary of the design guidelines youoften talk about, related to identities, roles, components, and distribution?

 

A. This question has come to me on several occasions,based on conference talks, articles, and classes I teach on various aspects ofASP.NET security. There are many resources out there describing various aspectsof ASP.NET security, but not many that collate the information related toarchitecture design and deployment. Beyond the many detailed aspects ofsecurity (validation, SQL injection protection, custom error pages, and more),there are also many design decisions that affect your approach to componentallocation and deployment. These are the concepts I will focus on in thisarticle.

 

First, let me establish a high-level list of designinfluences that relate to security; then I ll attack each separately. Thefollowing topics should be considered in each and every Web site deployment:

  • Role-based Security

  • Strongly Named Assemblies

  • Code Access Security

  • Run-time Identities

  • Component Distribution

 

Let s dig in!

 

Role-based Security

Of course, the first stop for securing a Web applicationis to authenticate users and authorize their access to content, functionality,and other resources. The most common forms of authentication are to let IISauthenticate users by gathering Windows credentials, or to configure IIS foranonymous access and gather your own credentials from a login page. Afterauthenticating the user and gathering their roles, we can later authorize theiraccess to content, resources, functionality, and more. So, the most importantaspect of the authentication step is that it attaches a security principal tothe request thread (and the HttpContext) so that role-based checks can do theirjob.

 

Security principals come in the form of WindowsPrincipalor GenericPrincipal. You can also implement a custom security principal byimplementing the IPrincipal interface. Each principal holds information aboutthe user s identity (WindowsIdentity, GenericIdentity, custom IIdentityimplementation) and its roles. How the security principal is attached to thethread is usually handled for you through Windows authentication, or the formsauthentication process. The ASP.NET Login control automatically attaches aGenericPrincipal to the thread after authenticating the user through theconfigured ASP.NET membership and role providers. Regardless of the roleprovider you use, you can use role-based authorization to do the following:

  • Restrict access to pages, directories, and otherfile resources protected by ASP.NET security using the section of the web.config.

  • Control what content is presented within a pageor control using role-aware controls like SiteMapProvider or LoginView, or byprogrammatically checking the security principal s role.

  • Limit access to component functionality fortypes, operations, or more granular code segments using permission demands.

 

The first two of these are well described in numerous Webresources. The latter, surprisingly, is not applied often enough, so I willreview the concept here.

 

Business components supporting your Web applicationrepresent an API layer to access a specific set of functionality. At thebusiness tier, business rules drive access to features and functionality. So,you can (and should) protect access to those components using permissiondemands. Let s take a look at the example illustrated in Figure 1.

 


Figure 1: Role-based securityapplied at several levels in an ASP.NET application.

 

The PhotoUploadManager component provides three functions:

  • UploadPhoto, accessible only to Administrators.

  • GetPhotos, accessible to Users and Guests.

  • GetPhotoDetail, accessible only to Users.

 

PrincipalPermissionAttribute is applied to each method toprevent unauthorized access to the feature, as shown in Figure 2.

 

_

Public Shared Sub UploadPhoto(ByRef imageInfo as ImageInfo)

...

End Sub

_

_

Public Shared Function GetPhotos() As ImageInfo()

...

End Function

_

Public Shared Function GetPhotoDetail(ByVal id as Long) AsImageInfo

...

End Function

Figure 2: PrincipalPermissionAttributeis applied to each method to prevent unauthorized access to the feature.

 

You can also see from Figure 1 that the role-basedsecurity constraints placed at the user interface tier are less restrictive.Any authenticated user is entitled to access default.aspx and display.aspx, butthe code behind the default page checks the user s role before forwarding themto the details page:

 

If System.Web.Security.Roles.IsUserInRole("User") Then

...

End If

 

By applying role-based security to your components, youcan ensure that they are not misused. This also enables you to have an entrypoint for auditing user access to specific features. In fact, even with Windowsauthentication, if the business layer has controls in place that restrictaccess based on roles, and if the security principal is attached to the thread,impersonation can be avoided without sacrificing security and auditingmeasures.

 

Strongly Named Assemblies

Before ASP.NET 2.0 you could not sign the applicationassembly. That means that Web application pages and user control code could becompromised through a malicious attack because friendly named assemblies arenot verified for integrity when the runtime loads them. Now, all source codethat comprises the ASP.NET application can be precompiled into strongly namedassemblies prior to deployment. For example, the following instructions tellthe ASP.NET compiler to pre-compile a Web site, give it a strong name using mlbkey.snk,and apply AllowPartiallyTrustedCallersAttribute to generated assemblies:

 

aspnet_compiler -v /MyWebSite -u -f c:MyWebSiteDeploy

 -keyfile mlbkey.

snk -fixednames -aptca

 

An ASP.NET application deployment should have thefollowing source tiers:

  • Inline page code for Web forms, user controls,and master pages, plus other files, such as resources (.resx), graphics, andXML.

  • Supporting code for pages (formerly known ascode-behind) and other source code placed in the App_Code directory.

  • Other assembly dependencies, including yourbusiness and data access components (although the latter should be accessedthrough business components, in a service-oriented environment).

 

In an enterprise site, inline page code should be avoidedand supporting page code (that is compiled to assemblies) should not includebusiness logic. Instead, they should coordinate calls to business componentsthat are separately signed and deployed to the global assembly cache (GAC).GAC-deployed assemblies load more quickly, but they are also verified forintegrity upon insertion to the GAC. If you properly protect the private keyused to sign the application assemblies, the risk of those assemblies beingtampered with is reduced significantly. The GAC must also be protected through propermanagement of the administrative accounts with access to the machine. Not tomention, skip verification should never be enabled for assemblies, with theexception of your development machine when you are using delay-signingtechniques.

 

Code Access Security

Code access security makes it possible to restrict what acomponent is allowed to do, regardless of the logged-in user or securityprincipal. There are a number of places where code access security must beconsidered in an ASP.NET application, including:

  • At the Web resource level, for inline or bindingcode executed by Web forms, user controls, and master pages.

  • At the application assembly level, for the codesupporting each Web resource.

  • At the business and data tier, for assembliesthat support the application.

 

You should reduce the privileges of the ASP.NET resourcetier (really, the application domain) by setting the element toLow (or Medium at most):

 

 

This means that no malicious code added to .aspx fileswill be able to invoke business components or .NET assemblies that requirepermissions beyond the setting. This implies that the application assembliesgenerated when you pre-compile should be signed, and includeAllowPartiallyTrustedCallersAttribute (otherwise, the lower trust pages can tinvoke the code, and that wouldn t be much use).

 

Your business and data tier components should be signed,installed to the GAC, and granted FullTrust permission using the Microsoft .NETFramework 2.0 Configuration MMC snap-in (see Figure 3). In fact, your machinepolicy should only include My_Computer_Zone with Microsoft_Strong_Name andECMA_Strong_Name and YOURCOMPANY_Strong_Name. All other code groups should bedeleted from the policy; therefore, other assemblies outside this range willnot be trusted. If you require third-party assemblies for your site tofunction, you can add a code group for them, as well. (NOTE: Any third partywithout a strong name is a third party to avoid.)

 


Figure 3: Code access securityelevation for trusted assemblies based on their strong name.

 

By granting FullTrust to assemblies, your applicationdepends on (and trusts) they will be able to function: access the file systemor registry, perform network activities, decrypt configuration settings(DPAPI), and so on. The snag you might run into is when one of those assembliesdoesn t allow partially trusted callers. Of course, any stack walk will fail atthe ASP.NET resource tier because you reduced the code access securityconfiguration to thwart attacks.

 

To work around this issue, you can assert FullTrust for avery short period (long enough to make the call that requires it). You can onlyassert permissions you have been granted, and it will prevent the stack walkfrom failing:

 

Dim namedSet as NamedPermissionSet = new NamedPermissionSet(

 "FullTrust",PermissionState.Unrestricted)

namedSet.Assert()

...do restricted work

namedSet.Revert()

 

This reduces the window of opportunity for a maliciousattack.

 

Run-time Identities

I often hear people confused by the concept of processidentity and security principal, so I d like to review that here. First of all,each process runs under a particular process identity. In the case of ASP.NETapplication code, it is hosted by the ASP.NET worker process, and thereforeruns with the assigned ASP.NET identity (usually ASPNET or NETWORK SERVICE).This identity has nothing to do with the user who authenticated into theapplication.

 

When a Windows user is authenticated by IIS, or when theLogin control authenticates a user, a security principal is attached to thethread, and the User object is set for the HttpContext. The security principalis used for role-based security checks. That s it. The Windows principal,however, governs what the process has access to in terms of resources, likeregistry, file system, network, database, and more. Regardless of the codeaccess rights granted to the application assemblies, if the process identity isnot entitled to access a particular resource, an exception will occur. Ofcourse! Windows security is first and foremost. Code access security is anadditional measure of protection that helps in cases where a hacker may havegained control of a Windows account.

 

You always want the ASP.NET worker process to run with restrictedrights. It should not be entitled to access any database, network storage ... really,any useful resource. The way to gain access to protected resources is to a)impersonate an account for a short duration to make the call, or b) call acomponent that runs in a separate process that has those rights. Figure 4illustrates the first scenario; Figure 5 illustrates the second.

 


Figure 4: Run-time impersonation forshort durations narrow the window for an attacker to access the protectedresource.

 


Figure 5: Distributing functionalityacross process and machine boundaries.

 

To achieve the first scenario you would leverage LogonUserand ImpersonateWindowsIdentity from the Win32 API, to handle impersonation.This requires asserting code access permissions to make the call, and thenreverting the assertion, and the impersonation context, when you are finished(see Figure 6).

 

Dim principal as WindowsPrincipal = Nothing

Dim context as WindowsImpersonationContext = Nothing

Dim secPerm as SecurityPermission = new SecurityPermission(

 SecurityPermissionFlag.UnmanagedCode|

 SecurityPermissionFlag.ControlPrincipal)

secPerm.Assert()

principal = GetLogonUser(domain, userName, password);

ImpersonateWindowsIdentity(principal.Identity)

CodeAccessPermission.RevertAssert()

...access protected resources

context.Undo()

Figure 6: LeverageLogonUser and ImpersonateWindowsIdentity.

 

Component Distribution

The problem with the first scenario in the previoussection is that, in order to assert and impersonate, it litters your code witha lot of clutter unrelated to the business logic. For smaller Web applicationswhere the entire site is hosted in a single physical tier, this at leastprovides a reduced attack surface by limiting the impersonation window. But, ina larger service-oriented application, you would design your business tier sothat each major chunk of business functionality could function whether it isaccessed in-process, out-of-process, or across machine boundaries. BothEnterprise Services and Windows Communication Foundation (WCF) make it possibleto wrap your business logic in this way, and, without modification to clientcode, distribute and configure as you wish.

 

Process and machine boundaries also imply securityboundaries. They put another layer between your resources and the entry pointto the application. Serviced components and services can play host for businesscomponents that must execute with a particular process identity, authenticatecallers to that process, and successfully access resources that can authorizethe calling process identity no impersonation is required (see Figure 5,which illustrates this point).

 

Crossing process and machine boundaries does carry aperformance penalty, and yet can on a wider scale increase the overallthroughput of the application by allowing it to scale out more flexibly. Ofcourse, performance is part of a different conversation altogether, soregarding security, this model of hosting services with a process identityappropriate for their functionality can help you decouple security requirementsfrom the application code, and leave configuration more flexible, at that.

 

Conclusion

As important as more granular efforts to secure yourapplication are, the concept of designing with security in mind is equallyimportant. It is through the combined effort of sound network andinfrastructure security, best practice coding efforts, component design, anddeployment configuration that your ASP.NET applications will be secured. Thisarticle focused on some specific items related to design, which may also piqueyour interest in the subjects of code access security, role-based permissiondemands, service orientation, and component distribution. The readme file inthe code download provides several resources to follow up on from thisoverview, including reference to the more granular security measures you cantake to protect your application. Enjoy!

 

If you have questions or comments regarding this column, orany other ASP.NET topics, please drop me a line at [email protected].Thanks for reading!

 

C# and VB.NET codeexamples are available for download.

 

Michele LerouxBustamante is Chief Architect at IDesign Inc., Microsoft Regional Directorfor San Diego, Microsoft MVP for XML Web services, and a BEA TechnicalDirector. At IDesign Michele provides training, mentoring, and high-endarchitecture consulting services, specializing in scalable and secure .NETarchitecture design, globalization, Web services, and interoperability withJava platforms. She is a board member for the International Association ofSoftware Architects (IASA), a frequent conference presenter, conference chairof SD s Web Services track, and a frequently published author. She is currentlywriting a book for O Reilly on the Windows Communication Foundation. Reach herat http://www.idesign.net or http://www.dasblonde.net.

 

Additional Resources

IDesign: http://www.idesign.net

Michele s blog: http://www.dasblonde.net

Michele s WCF book: http://www.thatindigogirl.com

 

 

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like