Security

Security is a very important topic to any program, especially ASP.NET applications, because you are essentially allowing any one in the world to access your server's file system. Even with desktop-based applications, you are often exposed to unknown and potentially threatening code (as demonstrated by the prolificacy of computer viruses in today's world).

The CLR takes a number of precautions to prevent unauthorized access by applications or users. In addition, the security framework is consolidated and integrated into .NET, so any application can use the same concepts and objects to prevent misuse. Now you'll take a deeper look into how security works with .NET.

Basic Concepts

Everything in .NET security is based on permissions. Code must have permission to access certain resources, users must have certain permissions to access certain code, and so on. Everything you do in the .NET Framework, whether you realize it or not, requires permissions. The .NET Framework even provides a number of objects that represent permissions.

Permissions are determined from a number of sources. User permissions can be determined by the credentials supplied when identification occurs, or by the role that the user fulfills. Code can be granted permissions based on who executes it, and security policies written by administrators.

There are three different types of permissions: code access, identity, and role-based. Each of these fulfills a specific function in the .NET Framework. Code access is used to determine the code's access to file system resources, such as files or environment variables. Identity permissions apply to assemblies, and determine what resources they have access to. This is done by examining features of the assembly (called evidence), such as where the assembly originated from, digital signatures, and so on. Finally, role-based permissions determine user-related access. You'll examine code access and role-based permissions later in the chapter.

Finally, after permissions are established, .NET can proceed with authentication and authorization. The former is the process of verifying the requesters are who they say they are. The latter is the process of allowing or denying authenticated users access to resources. These two concepts apply heavily to ASP.NET applications, and you'll examine them in more detail later in the chapter.

Code Access Security

Code access security is a new concept introduced by .NET. This system determines what resources a particular application or piece of code has access to. This might not seem revolutionary, but first you need to see how security was implemented prior to .NET.

Security used to be based on user identities. In other words, a user tries to access resources by supplying valid credentials (such as a username and password) and then is given varying permissions based on those credentials. A major flaw with this system is that even trusted users can execute malicious code, whether intentionally or not, that causes havoc to an operating system. This explains why so many users have been infected by computer viruses.

For example, just because you log in using the administrative account on your computer doesn't mean that you won't accidentally open an attachment in an e-mail message that is actually a virus. Even worse, hackers might gain access to such administrative accounts and execute all sorts of unruly things.

Code access security solves these issues by not relying on user identities and permissions. Rather, the code that is executed is granted permissions based on its credentials, which tend to be harder to fake than user credentials. These include the code's source and digital signatures.

In this situation, if an administrative user attempts to open an unauthenticated e-mail attachment, the request fails, even though the user is an administrator. The user must specifically override the settings to allow this action to be performed. Although code access doesn't stop this occurrence, it does prevent code from being executed unknowingly or unintentionally.

You can write code without targeting the .NET code access system; in fact, you've been doing so for the past 12 chapters. However, you won't be able to provide information about your code to the runtime, leaving interpretation to the CLR, and therefore it might be denied security access. Thus, you can use special attributes in your code to specifically request permissions, as you'll see in the next two sections.

Requesting Permissions from the Runtime

Ideally, it should not be left to the CLR to determine what permissions your code should have access to. Thus, you can specify upfront the permissions your application will need. Note that this doesn't necessarily mean that your application will get those permissions—all the rules of code access security still apply—but it helps to ensure that your code cannot be used maliciously and will operate properly in tight security situations.

There are three ways you can request permissions: request the minimum allowed by the CLR, request optional permissions that are not crucial to the functionality of your application, and explicitly deny permissions, even if the CLR will grant them. All of these methods are performed with nearly the same syntax:

<assembly: PermissionObject(SecurityAction.method,
							Flags := flags)>

This code must be placed at the assembly level. Listing 13.6 shows an example that requests the FileIOPermission, which allows the application to read and write to the file system.

Listing 13.6. Requesting Permissions Explicitly
Imports System
Imports System.Security
Imports System.Security.Permissions
<assembly: FileIOPermission(SecurityAction.RequestMinimum, Unrestricted := True)>

Namespace MyNamespace
   Public Class MyHappySecureClass
      Public Shared Sub Main()
         'do file system access here
      End Sub
   End Class
End Namespace

This listing looks just like any other code you've seen, except for the assembly attribute after the Imports statements. This code requests the minimum security file I/O permissions allowable by the CLR, specifying that the access is unrestricted. If you wanted to optionally request the file I/O permission, change the fourth line to read:

<assembly: FileIOPermission(SecurityAction.RequestOptional, Unrestricted := True)>

And if you want to disallow your own code to access the file system, use the following:

<assembly: FileIOPermission(SecurityAction.RequestRefuse, Unrestricted := True)>

When your application is compiled, it places this assembly attribute information into the assembly manifest (that is, the metadata), which is then examined by the CLR during load.

Additionally, rather than requesting individual permissions, you can request permission sets. These sets are built-in groups of permissions that fit different requirements. The syntax is

<assembly: PermissionSetAttribute(SecurityAction.method, Name := "set_name")>

Table 13.2 lists the built-in permission sets.

Table 13.2. Built-in Permission Sets
Name Description
Execution Permission to execute, but not to access protected resources
FullTrust Full access to all resources
Internet The permission to use for content from an unknown source
LocalInternet The permission set to use content from an unknown source, but from within an enterprise
Nothing No permissions (do not execute)
SkipVerification Try to skip permission verification

Requiring Permissions of Your Users

In addition to informing the CLR of the permissions your application needs at runtime, you can also require that users of your application have certain permissions during execution. Just because your application was granted the unrestricted FileIOPermission permission doesn't necessarily mean that you want to enable the user of your code to delete any and all files. Thus, anywhere in your code, you can require users to have permissions before execution will occur.

This method of declaring security is very useful when you don't really know beforehand what resources the user will be accessing. For example, in the case of Microsoft Word, you initially want to obtain permissions to access the file system, and allow the users to open and write files. However, you do not want them to overwrite certain crucial files, such as system files. Because you have no way of knowing what files the users will try to access, you should implement security procedures in your application in the places where unauthorized access could occur.

There are two methods to do this: declarative and imperative. The first, declarative security, can be used only at the assembly, class, or member level. This means that certain permissions must be met before any of the code is executed. Imperative security methods can be placed anywhere in your code, and are very useful when you want your code to conditionally execute based on information obtained during execution. You'll look at the declarative security syntax first.

The syntax for this method is very similar to requesting permissions from the CLR. Place the following code before any member that you want to protect:

<MyPermission(SecurityAction.Demand, flag = flags)>

Replace MyPermission with the appropriate permission object, and use SecurityAction.Demand to indicate that this particular permission must be met before any of the code will be executed. Again using the FileIOPermission as an example, Listing 13.7 shows a code snippet from a typical class.

Listing 13.7. Using Declarative Security Permissions
<FileIOPermission(SecurityAction.Demand, Unrestricted = True)>
Public Class MyClass
   Public Sub New()
      'The constructor is protected by the security call.
   End Sub

   Public Sub MyMethod()
      'This method is protected by the security call.
   End Sub

   Public Sub MyMethod2()
      'This method is protected by the security call.
   End Sub
End Class

Because the declarative attribute is placed at the class level, all methods within also require the specified permission. Similarly, you could place the attribute at the assembly or method level to affect an entire assembly or a single method, respectively.

Imperative security works a bit differently. It can be used only inside a method call, and you work with the permission objects directly. Listing 13.8 shows an example.

Listing 13.8. Using Imperative Security Permissions
Public Class MyClass
   Public Sub MyMethod()
      'This method is protected by the security call.
      Dim objPerm as New FileIOPermission(Unrestricted)

      objPerm.Demand()
      'code
End Sub

   Public Sub MyMethod2()
      'This method is not protected by the security call.
   End Sub
End Class

In this code, you create a new FileIOPermission object, passing it the Unrestricted permission state variable. Then you simply call the Demand method to demand the user have security permission from that point forward. If the user does not have permission, an access denied message will be raised.

Role-Based Security

Role-based security is based on granting permissions to users based on the roles they fulfill. For example, you might perform the administrative role on your own computer, but are only a power user on your computer at work. The role you perform is granted certain permissions, and therefore, multiple people can be in each administrative or power user role without having to assign individual permissions.

Role-based security in .NET is based on two key concepts: identities and principals. They are very similar, but it is important to see the distinctions. An identity represents a user; it contains information about the user's name, password, validation type, and so on. A principal, on the other hand, represents the application or code and its permissions. Thus, there are two security checks for role-based security—one based on the user, and one based on the code being executed. Both are necessary to ensure security of your operating system.

.NET provides two built-in versions of each of these objects: generic (the GenericIdentity and GenericPrincipal objects) and Windows (the WindowsIdentity and WindowsPrincipal objects). The generic objects are provided so that you can implement your own custom security checks after obtaining the user's and code's credentials. The Windows-based objects are used when you want to rely on Windows authentication to dole out permissions. No matter the objects you use, the syntax is still the same. Listing 13.9 shows a very simple application that performs role-based security checks.

Listing 13.9. Role-Based Security Application
Imports System
Imports System.Security.Permissions
Imports System.Security.Principal
Imports System.Threading

Public Class RoleCheck
   Public Shared Sub Main()
      Dim strUserName, strPassword as String

      Console.Write("Enter your user name: ")
      strUserName = Console.Readline()

      Console.Write("Enter your password: ")
      strPassword = Console.Readline()

      if strUserName = "JoeSmith" AND strPassword = "password" Then
         Console.Writeline("Permission granted")
         Dim arrUserInfo As String() = { "Administrator", "User"}
         Dim objIdentity as New GenericIdentity("JoeSmith")
         Dim objPrincipal as New GenericPrincipal(objIdentity, arrUserInfo)
         Thread.CurrentPrincipal = objPrincipal
      end if
   end sub
end class

Performing role-based security involves only a few steps in addition to what you already know. First, you import the System.Security.Permissions and System.Security.Principal namespaces.

The first part of the code should look familiar. You're simply displaying output to the user, and reading in values from the command line. Next, you have a simple If...Then...Else statement that checks whether the supplied credentials match the strings JoeSmith and password. You then use Console.Writeline to write a response to the user. After the Console.Writeline, however, is where it starts to get interesting.

Using the GenericIdentity object, you instantiate a new identity named JoeSmith. Next, you use this identity object to create a new GenericPrincipal object, supplying an array of role information (this array holds the names of the roles this identity fulfills).

Finally, after you've created your principal and identity objects, you need to make sure that the rest of the application executes under the specified permissions. You do this by setting the CurrentPrincipal property of the Thread object to the principal you just created. Now, anything that executes in this application after you set the thread's CurrentPrincipal property will execute using the identity and principal declared here. (See Chapter 11, “Creating Multithreaded Visual Basic .NET Applications,” for more information on threads.)

For example, the following code snippet could be placed anywhere else in the listing, and security checks would be performed:

Sub MyProtectedMethod()
   Dim objPermission as New PrincipalPermission("JoeSmith", "Administrator")
   objPermission.Demand()

   'perform secure functionality
End Sub

This method creates a new permission object based on the identity JoeSmith, with the role Administrator. Then you call the Demand method just as you did with imperative security. If you had not granted the Administrator role to the identity JoeSmith in Listing 13.9, this code snippet would not execute. (The PrincipalPermission object on the second line is just another permission object, much like FileIOPermission.)

You can also perform role-based declarative security checks with the syntax:

<PrincipalPermissionAttribute(SecurityAction.Demand, Name:="JoeSmith", Role:="Administrator")>

Using the identity and principal objects, you can ensure the utmost security for your application no matter who accesses it.

ASP.NET Security

Security concepts in ASP.NET are very similar to VB .NET desktop-based applications; a user assumes an identity and supplies credentials, and is given certain permissions based upon them. This should now be old hat to you. The methods of securing your applications, however, are very different in ASP.NET.

ASP.NET relies on three steps to secure its applications. The first step, authentication, is the process of making sure that a user is who she says she is. This is the same concept as in VB .NET security. The second step, authorization, is determining the resources authenticated users have access to—again, the same concept from VB .NET. The third step, impersonation, however, is a new concept. All these methods can be implemented through the web.config file.

Impersonation allows the ASP.NET engine to impersonate the user that accesses an application, thereby restricting its own power. For example, in a typical Web site, anonymous visitors are allowed access. When such an anonymous visitor starts executing ASP.NET pages, the pages are executed with the visitor's permission set, rather than ASP.NET's permission set, which has many more permissions than an anonymous user. This prevents visitors from attempting to access resources that they shouldn't be able to by hacking ASP.NET, much as code access security protects users from inadvertently executing malicious code.

Next, we'll examine the three steps to ASP.NET security, and how each is implemented.

Authentication

You can implement authentication with just one additional line to your web.config file. The syntax for implementing authentication is as follows:

<configuration>
   <system.web>
      <authentication mode="mode" />
   </system.web>
</configuration>

There are three modes ASP.NET can use to implement authentication. The first, Windows, relies on IIS and Windows authentication. This methodology is the same as used to secure any ASP.NET application, so we won't cover it here. See Sams Teach Yourself ASP.NET in 21 Days for more information.

Passport authentication is the second method. This relies on Microsoft's Passport technology—a central location of user information used for validation—to function. This allows a user to log in to one Passport-enabled site, and automatically be logged in to every Passport site. It works similarly to Forms authentication, which will be discussed in a moment. To use Passport authentication, you must first register with the Passport service (this requires paying a fee, which explains why it won't be covered in this book). Then when users visit your site, they will first be redirected to Microsoft's Passport login form and, if valid credentials are supplied, will be redirected back to your secure pages. Visit www.passport.com for more information.

The third method, Forms authentication, is very common. You've probably seen Web sites that present you with a form for username and password information—this is Forms-based authentication. You enable this method with the following web.config file:

<configuration>
   <system.web>
      <authentication mode="Forms">
         <forms name="name" loginUrl="url"/>
      </authentication>
   </system.web>
</configuration>

When a user now attempts to access your Web site, he will first be redirected to the URL specified by the loginUrl property. This page should contain a login form for the user to enter credentials. Using the FormsAuthentication object, you can then verify a user's identity. Listing 13.10 shows an ASP.NET page that provides a login form and validates a user upon submission.

Listing 13.10. A Forms Authentication ASP.NET Page
<%@ Page Language="VB"%>

<script runat="server">
   sub Login(obj as Object, e as EventArgs)
      if tbUserName.Text = "JoeSmith" and tbPassword.Text = "password" then
         FormsAuthentication.SetAuthCookie(tbUsername.Text, false)
         Response.Redirect("securepage.aspx")
      else
         lblMessage.Text = "<font color=red>Sorry, invalid username or " & _
            "password!</font><p>"
      end if
   end sub
</script>

<html><body>
   Please enter your username and password.<p>
   <form runat="server">
      <asp:Label id="lblMessage" runat="server"/>
      Username:
      <asp:Textbox id="tbUserName" runat="server" /><br>
      Password:
      <asp:Textbox id="tbPassword" TextMode="password" runat="server" /><p>
      <asp:Button id="Submit" runat="server" OnClick="Login" Text="Submit"/>
   </form>
</body></html>

The lines inside the <form>...</form> tags simply present a form to the user to enter a username and password. When the user presses the submit button created as an ASP.NET server control, the form posts back to itself and executes the Login method.

The code in the Login procedure is a simple check of the supplied credentials. Here, the username and password must be “JoeSmith” and “password,” respectively. The next line contains the only new code in this listing: You call the SetAuthCookie method of the Forms Authentication object to set a cookie on the visitor's computer to indicate that he is a valid user and is allowed access to the site. The first parameter of this method is the username the visitor will be identified by in your application, and the second indicates whether this authentication cookie should persist after the visitor leaves your site. False means that the user will have to log in every time he visits, whereas True means that he has to log in only the first time; all subsequent visits will use the same authentication cookie to validate the user. Then, you simply redirect the user to the secure part of your site.

If the validation fails, you display an error message if the credentials are invalid. Figure 13.7 shows the output of this page when the credentials don't match the hard-coded values.

Figure 13.7. Access is denied with the page fails the hard-coded security values.


You can also supply valid username and passwords in your web.config file with the following syntax:

<configuration>
   <system.web>
      <authentication mode="Forms">
         <forms name=AuthCookie" loginUrl="login.aspx">
            <credentials passwordFormat="format">
               <user name="clpayne" password="helloworld" />
            </credentials>
         </forms>
      </authentication>
   </system.web>
</configuration>

The password format indicates whether the password should be encrypted before being sent. The Clear format indicates no encryption, and MD5 and SHA1, indicate well-known encryption schemes.

If you specify user credentials in your web.config file, you'll have to modify your login page slightly. Change the Authenticate call to the following line:

if FormsAuthentication.Authenticate(tbUserName.Text, tbPassword.Text) then

The Authenticate method compares the supplied username and password to the web.config file.

Authorization

Recall that authorization is the process of determining whether users have the necessary permissions to access resources, after they have been authenticated. Like authentication, authorization can be implemented in a number of different ways. You can use file-based authorization, which relies on Windows NT Access Control Lists (ACLs). These are permission lists maintained by the operating system that specify which users have access to which files. An ACL can be created for every single file and folder on your computer, which makes it a very powerful and flexible security system. See the Windows documentation for more information on using ACLs.

URL authorization, on the other hand, can be implemented through your web.config file. This method relies on the hierarchical configuration scheme of web.config, and denies or allows access to files and folders based on the URL path.

For example, enabling authorization in your web.config file at your root Web directory will enable authorization for all files in your Web directory, including subdirectories. These values can be overridden by other web.config files in those subdirectories, just as the rest of the settings in web.config.

To enable authorization, place the following XML elements in your <system.web> web.config element:

<authorization>
   <allow users="comma-separated list of users"
          roles="comma-separated list of roles" />
   <deny users="comma-separated list of users"
         roles="comma-separated list of roles" />
</authorization>

Simply place the users and roles you want to allow or deny in the appropriate allow or deny element. That's all there is to it!

You can also use special wildcards to allow or deny groups of users at once. The ? operator means allow or deny all anonymous users, and * means allow or deny all users. The default behavior when authorization is not otherwise specified is to allow all users:

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

Impersonation

Impersonation can be a difficult concept to comprehend. As discussed earlier, it allows ASP.NET to execute code under a specified user's identity.

By default, impersonation is disabled. As the user moves from IIS authentication to the ASP.NET application, ASP.NET itself takes on the identity that IIS is configured to use (by default, this is the local System identity). Typically, this identity has permissions to access all files and folders. Other security measures must be used to handle access, such as URL authorization.

When impersonation is enabled, ASP.NET takes on the role of the identity that IIS passes to it. If the user is unauthenticated, ASP.NET will impersonate the anonymous user, and if the user is authenticated, ASP.NET will take on that identity. Now that ASP.NET is impersonating another user, Windows can restrict access to the application as a whole by using ACLs.

An ASP.NET application is a user of the system's resources. It accesses files and folders, memory, and so on. By default, the ASP.NET application has fairly liberal permissions, and generally can access everything the system has to offer. This is necessary because ASP.NET must use these resources to operate correctly. However, you might want to restrict access to certain resources, depending on who's using the ASP.NET application. For example, an anonymous user shouldn't be able to take advantage of the application's permissions and access all the system's resources. Thus, an ASP.NET application can impersonate its users in order to restrict access.

To enable impersonation, you need to add only one line to your web.config file, under the <system.web> element:

<identity impersonate="true" username="user" password="pw" />

The username and password attributes are optional, and can be used if you want ASP.NET to impersonate a specific identity, other than the one that IIS passes to it.

For more information on any of these ASP.NET security concepts, check out Sams Teach Yourself ASP.NET in 21 Days.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.252.238