Enterprise Security Scenarios

I l @ ve RuBoard

The following sections offer enterprise application development scenarios that implement the concepts we've discussed thus far in the chapter.

Code Security

Windows security is based on a user 's identity, and code access security is based on an assembly's identity. We'll delve into two important aspects of the Visual Basic .NET code security model: code access security and requesting code permissions.

Code Access Security

Code access security is all about control access to assemblies (or individual methods contained in assemblies). Every assembly loaded by the CLR is assigned evidence that describes its identity. This evidence can be the path or URL from which the assembly was loaded, or it can be a digital signature given to the code by its publisher. To control access to your code, you can simply demand that your caller have a specific identity. For example, to limit access to a shared component to only code from the same publisher, the publisher will sign all its code and then place a demand for that signature within its shared component.

The identity permissions found under the System.Security.Permissions namespace are used for this purpose. Identity permissions are provided for the following types of assembly identity: strong name , Authenticode publisher certificate, URL of origin, site of origin, and Internet Explorer security zone. All identity permissions support three types of identity demands, which are described in Table 9-1.

Table 9-1. Types of Identity Demands

Identity

Description

Demand

All callers on the call stack are required to have the specified identity. This check is performed at run time.

LinkDemand

Only the code's immediate caller is required to have the specified identity. The caller must be trusted to not allow misuse of the called code by its own callers. This check is performed during loading.

InheritanceDemand

Requires the specified identity of any code that attempts to inherit from or override a method on the protected code. This check is performed during loading.

Because LinkDemand and InheritanceDemand are performed when assemblies are loaded, they can be specified only declaratively using attributes at compile-time. The compiler places the declarative security in an assembly's manifest, where the CLR can read and act upon it when loading that assembly. A straight demand, on the other hand, occurs at run time and can be specified imperatively or declaratively .

The following example demonstrates how to make a link demand on a method based on a strong-named method. The public key has been abbreviated for readability.

 <StrongNameIdentityPermission(SecurityAction.LinkDemand,_ PublicKey:= "002400000...")>_ PublicSharedSubProtectedMethod() 'dosomething EndSub 

The StrongNameIdentityPermission attribute supports the additional properties Name and Version . By specifying Name , Version , and PublicKey , a client can reliably demand an exact version of an assembly. If the client specifies only Name and PublicKey , the demand will succeed if the assembly name and signature match, regardless of the assembly version. If the client specifies only PublicKey , as in the code example above, the security system will look for only the required signature; this is useful when you want to limit access to a group of code signed by the same key.

Attaching a strong name signature to your code involves two steps: creating the strong name key and compiling your assembly with that key. The first step is accomplished by using the SN (strong name) utility provided with the .NET Framework SDK. Below is the command-line syntax for creating a key pair and viewing the public key portion. (You must make an identity demand for code signed with the corresponding private key.)

 sn-kkeypair.dat sn-pkeypair.datpublickey.dat sn-tppublickey.dat 

More Info

For more information about the strong name tool, see Chapter 8.


The second step requires adding a declaration to the assembly to indicate the location of the file generated in step 1:

 <Assembly:AssemblyKeyFile("keypair.dat")> PublicClassMyClass 'somethinginteresting EndClass 

You can also delay-sign an assembly. Delayed signing reserves room for the signature in an assembly's manifest but does not actually sign the assembly. It is used when the author of the assembly does not have access to the private key that will be used to generate the signature. You can implement delayed signing by using the AssemblyDelaySign attribute class.

More Info

For more information about delayed signing, check out the Framework SDK documentation that accompanies Visual Studio .NET.


Requesting Code Permissions

You do not necessarily have control over what permissions are assigned to the code you write, so the CLR provides a mechanism for requesting the permissions code needs in order to run properly. If the code is not granted the required permissions, it will not run. And, because permission requests are stored in an assembly's manifest, the end user can run the SDK tool called PERMVIEW to determine what permissions have been requested by the assembly author and then take the appropriate steps to grant those permissions if she needs the code to run on her machine. Table 9-2 lists the three types of permission requests supported by the .NET runtime.

Table 9-2. Permission Request Types

Type

Description

RequestMinimum

The permissions the code must have to run properly. If these permissions cannot be granted, the code will not be executed.

RequestOptional

The permissions that should be granted if allowed by policy. The runtime will attempt to execute code even if permissions it requests as optional have not been granted.

RequestRefuse

The permissions that code should never be granted. Code will not receive these permissions even if they would normally be granted to it. This is an extra precaution you can take to prevent your code from being misused.

Permission requests can be made only in a declarative fashion and must always be at the assembly level. (The assembly is the unit to which permissions are granted by the security system.) The following code is a request stating that an assembly must have unrestricted access to the file system in order to function:

 <Assembly:FileIOPermission(SecurityAction.RequestMinimum,_ Unrestricted:=True)> PublicClassFileMover 'somethinginteresting EndClass 

You can make several requests of the same type, in which case the final permission set requested will be the aggregate of all requests of that type. In the following example, RequestMinimum is used twice with different permissions to state that the assembly must have the ability to use Reflection Emit and perform serialization in order for it to function.

 <Assembly:ReflectionPermission(SecurityAction.RequestMinimum,_ ReflectionEmit:=True)> <Assembly:SecurityPermission(SecurityAction.RequestMinimum,_ SerializationFormatter:=True)> PublicClassCodeGenerator 'somethinginteresting EndClass 

The same permission can also appear in requests of different types. For instance, the following example program uses an EnvironmentPermission in each of its three requests ( Minimum , Optional , and Refuse ). Using requests of different types is useful when a permission encompasses a number of operations and you want to ensure that your assembly has the ability to perform some of those operations while being prevented from performing others.

Note

Any permission you refuse using RequestRefuse will not be granted to your assembly even if you request that same permission using RequestMinimum .


In addition to requesting individual permissions, you can also request entire sets of permissions in a compact fashion. The following example shows two requests: one stating that an assembly must have unrestricted access to the file system in order to function, and one stating that it will take any and all other permissions that the security system is willing to grant it.

 <Assembly:FileIOPermission(SecurityAction.RequestMinimum,_ Unrestricted:=True)> <Assembly:PermissionSet(SecurityAction.RequestOptional,_ Name:= "FullTrust")> PublicClassFileMover 'somethinginteresting EndClass 

The example shows how to request a permission set by name, but it is also possible to use a custom permission set representing the exact permissions you want. For more information on how to do this, search on PermissionSetAttribute in the .NET Framework SDK Reference.

The PERMVIEW tool is useful for verifying that your permission requests are correct. You can run PERMVIEW on a compiled assembly to read the permission requests out of the assembly's manifest and display them, as shown here:

 C:\>permviewfilemover.exe Microsoft(R).NETFrameworkPermissionRequestViewer.Version1.0.XXXX.0 Copyright(C)MicrosoftCorp.1998-2001 minimalpermissionset: <PermissionSetclass="System.Security.PermissionSet" version="1"> <IPermissionclass="System.Security.Permissions.FileIOPermission" version="1" Unrestricted="true"/> </PermissionSet> optionalpermissionset: <PermissionSetclass="System.Security.PermissionSet" version="1" Unrestricted="true"/> refusedpermissionset: Notspecified 

User Identity

User identity is a common means of controlling access to a business application or limiting the options available within that application. The System.Security.Principal namespace contains classes that help make such role-based security determinations. These classes are highly extensible. They allow host code to provide its own user identity and role information, or they allow it to expose the user account and group information provided by Windows. For more complete details regarding how to extend the role-based security system, consult the .NET Framework SDK Developer's Guide.

If you simply need to check the user's Windows user name and group memberships from a client application, here is how. The WindowsIdentity class represents an authenticated Windows user, and the WindowsPrincipal class that encapsulates the WindowsIdentity contains information about the user's role memberships. These objects representing the current user are accessible using either a static property of the Thread object or a static method of the WindowsIdentity object. We'll look at examples of both shortly.

Accessing the current principal from the Thread object is the standard approach, and it works for all types of principal objects. But because this method returns an IPrincipal , it must be cast as a WindowsPrincipal before it can be used as one. Notice that before the current principal is accessed, a call to SetPrincipalPolicy is made. This is noteworthy because without this call the principal returned would be a GenericPrincipal containing no user information. Because the call to SetPrincipalPolicy requires the ControlPrincipal Security ­Permission (one not normally given out to less-than -fully-trusted code), this prevents semitrusted code (such as that running off the Internet) from gaining access to a user's account name.

 'GettheCurrentUser'sSecurityPolicy AppDomain.CurrentDomain.SetPrincipalPolicy(_ PrincipalPolicy.WindowsPrincipal) DimuserAsWindowsPrincipal=_ CType(System.Threading.Thread.CurrentPrincipal,WindowsPrincipal) DimidentAsWindowsIdentity=user.Identity 

Checking for a Windows identity is a very common case, so this identity is easily accessible by using the static GetCurrent method of the WindowsIdentity class, as shown in the following example. Please note, however, that this method requires the same level of permission as the one above.

 DimidentAsWindowsIdentity=WindowsIdentity.GetCurrent() DimuserAsNewWindowsPrincipal(ident) 

Once a WindowsPrincipal object is retrieved, a user's group membership can be determined using the IsInRole method. If the goal of checking role group membership is to deny access to an application (as opposed to customizing the user experience), an even simpler approach is to use the Principal ­Permission to demand the required role.

Scripting Security

The .NET Framework SDK ships with some tools you can use to script user, machine, and enterprise security policies.

Scripting Security Policy Changes

The CLR ships with an advanced security policy system that allows for three policy levels: the enterprise policy, the machine policy, and the user policy. Each policy level consists of a tree of code groups. Each code group consists of a membership condition (which might be based on URL of origin or publisher certificate, for example) and an associated permission set. Code is granted the permission set associated with a code group if it meets the respective membership condition. By changing code groups in the user, machine, or enterprise policy, administrators can determine what permissions are granted to assemblies.

If you need to script policy changes, you can use the Code Access Security Policy ( Caspol .exe) command-line tool to create batch files containing policy change commands.

Note

For all standard administrative tasks , it is highly recommended that you use the Common Language Runtime Configuration (Mscorcfg.msc) tool. For more information on the policy system, see the security documentation in the Frameworks SDK.


The Code Access Security Policy (Caspol.exe) Utility

The Framework SDK comes with the Caspol command-line policy administration tool, which you can use to create batch files for scripting security policy changes. Type caspol -? at the command line to see the available options.

Scripting Against Named Code Groups

The CLR's default policy gives each code group a unique name. If code groups have not been deleted or renamed , you can uniquely script changes against these code groups. The most common code group names for scripting are listed in Table 9-3.

Table 9-3. Common Code Group Names for Scripting

Name

Description

All_Code

The root code group in every policy level

My_Computer_Zone

The code group that applies to code on local computer

Internet_Zone Code

The code group that applies to code from the Internet

LocalIntranet_Zone

The code group that applies to code from the intranet

To see a complete list of code groups and their names in all policy levels, you can use the following caspol command:

 caspol-all-listgroups 

To change a code group's permission set, include a command of the following form in your batch script:

 caspolPolicyLevel-chggroupNameofcodegroupPermissionSetName 

To add a new code group, include a command of the following form in your batch script:

 caspolPolicyLevel-addgroup<NameofParentcodegroup> MembershipConditionPermissionSetNameCodeGroupFlags 

To reset policy to the default state at a policy level, include a command of the following form in your batch script:

 caspolPolicyLevel-reset 

The following caspol batch script resets policy to the default on all policy levels and grants full trust to intranet applications. Because the granted permissions to code are calculated as the intersection between policy levels and (in default policy) both enterprise and user policy levels are set to full trust, you need to change only the machine policy level in order to guarantee that intranet applications receive full trust. In our example, the following script guarantees that intranet applications will run with full trust (while code from other places of origin will run with the permissions given it by default policy):

 caspol-all-reset caspol-machine-chggroupLocalIntranet_ZoneFullTrust 

The first line in the following script of caspol commands shows how to set policy so code from the Internet will not receive any permissions from machine policy. The second command shows how to add a code group for granting full trust to code signed by the publisher that signed Myexe.exe. Note that the new code group is hung off the root of the machine policy.

 caspol-machine-chggroupInternet_ZoneNothing caspol-machine-addgroupAll_Code-pub-fileMyexe.exeFullTrust 

Authentication and Authorization

Next, we'll look at .NET and operating system interaction in the areas of authentication and authorization.

Windows Identity in Server Applications

When you use ASP.NET Windows authentication, ASP.NET attaches a WindowsPrincipal object to the current request. This object is used for URL authorization. The application can also use it programmatically to determine whether a requesting identity is in a given role.

 IfUser.IsInRole("Administrators")Then DisplayPrivilegedContent() EndIf 

The WindowsPrincipal class determines the roles of the user's NT group membership. ASP.NET applications that want to determine their own roles can do so by handling the WindowsAuthentication_OnAuthenticate event in their Global.asax file and attaching their own class that implements System.Security.Principal.IPrincipal to the request, as shown in the following example:

 'CreateaclassthatimplementsIPrincipal PublicClassMyPrincipal:ImplementsIPrincipal 'Implementapplication-definedrolemappings EndClass 'InaGlobal.asaxfile PublicSubWindowsAuthentication_OnAuthenticate(SourceAsObject,_ eAsWindowsAuthenticationEventArgs) 'Attachanewapplication-definedclassthatimplementsIPrincipalto 'therequest. 'NotethatsinceIIShasalreadyperformedauthentication,theprovided 'identityisused. e.User=NewMyPrincipal(e.Identity) EndSub 
Forms-Based Authentication

Forms-based authentication is an ASP.NET authentication service that enables applications to provide their own logon user interface and do their own credential verification. ASP.NET authenticates users, redirecting unauthenticated users to the logon page and performing all the necessary cookie management. This sort of authentication is used by many Web sites.

An application must be configured to use forms-based authentication; you set <authentication> to Forms and deny access to anonymous users. The following example shows how this can be done in the Web.config file for the desired application:

 <configuration> <system.web> <authenticationmode="Forms"/> <authorization> <denyusers="?" /> </authorization> </system.web> </configuration> 

Administrators use forms-based authentication to configure the name of the cookie to use, the protection type, the URL to use for the logon page, the length of time the cookie will be in effect, and the path to use for the issued cookie. Table 9-4 shows the valid attributes for the <Forms> element, which is a subelement of the <authentication> element shown in the following example:

 <authenticationmode="Forms"> <formsname=".ASPXCOOKIEDEMO" loginUrl="login.aspx" protection=  "all" timeout="30" path="/"> <!--protection="[AllNoneEncryptionValidation]" --> </forms> </authentication> 
Table 9-4. Security-Related Forms Attributes

Attribute

Description

loginUrl

The URL of a page to which unauthenticated users are redirected. It usually contains the login interface or at least a message informing the user that she isn't authorized to use the form. This page can be on the same computer or a remote one. If it is on a remote computer, both computers must use the same value for the decryptionkey attribute.

Name

The name of the HTTP cookie to use for authentication purposes. If more than one application wants to use forms-based authentication services on a single computer, the applications should each configure a unique cookie value. To avoid causing dependencies in URLs, ASP.NET uses " /" as the Path value when setting authentication cookies so the cookies will be sent back to every application on the site.

timeout

An integer value that specifies the amount of time, minutes, after which the cookie will expire. The default value is 30. This attribute is a sliding value, expiring n minutes from the time the last request was received. To avoid adversely affecting performance and to avoid multiple browser warnings for those who have cookies warnings turned on, the cookie is updated if the time is more than half elapsed. (This means a loss of possible precision in some cases.)

Path

The path to use for the issued cookie. The default value is " /" to avoid difficulties with mismatched case in paths because browsers are case-sensitive when returning cookies. Applications in a shared-server environment should use the default path to maintain private cookies. (Alternatively, they can specify the path at run time using the APIs to issue cookies.)

Protection

The method used to protect cookie data. Valid values are:

All : Uses both data validation and encryption to protect the cookie. The configured data validation algorithm is based on the element. Triple DES is used for encryption, if available and if the key is long enough (48 bytes). All is the default (and recommended) value.

None : Use for sites that use cookies only for personalization and have weaker security requirements. Both encryption and validation can be disabled. You should use caution if you use cookies without encryption (because the cookie can be shared between sites that issue cookies to the client ” potentially infringing on the client's privacy), but this setting provides the best performance of any method of personalization using the .NET Framework.

Encryption : Encrypts the cookie using TripleDES or DES but doesn't perform data validation on the cookie.

Validation : Does not encrypt the contents of the cookie but validates that the cookie data has not been altered in transit. To create the cookie, the validation key is concatenated in a buffer with the cookie data and a MAC is computed and appended to the outgoing cookie.

After the application has been configured, you need to provide a logon page. The following example shows a simple logon page. When the sample is run, it requests the Default.aspx page. Unauthenticated requests are redirected to the logon page (Login.aspx), which presents a simple form that prompts for an e-mail address and a password. (Use Username and Password as the credentials.)

After validating the credentials, the application calls the following:

 FormsAuthentication.RedirectFromLoginPage(UserEmail.Value,_ PersistCookie.Checked) 

This redirects the user back to the originally requested URL. Applications that do not want to perform the redirection can call FormsAuthentication.GetAuthCookie to retrieve the cookie value or FormsAuthentication.SetAuthCookie to attach a properly encrypted cookie to the outgoing response. These techniques can be useful for applications that provide a logon user interface embedded in the containing page or that want to have more control over where users are redirected. Authentication cookies can be temporary or permanent (persistent). Temporary cookies last only for the duration of the current browser session. When the browser is closed, the cookie is lost. Permanent cookies are saved by the browser and are sent back across browser sessions unless explicitly deleted by the user.

The authentication cookie used by forms authentication consists of one version of the System.Web.Security.FormsAuthenticationTicket class. The information includes the username (but not the password), the version of forms authentication used, the date the cookie was issued, and a field for optional application-specific data.

Application code can revoke or remove authentication cookies using the FormsAuthentication.SignOut method, which removes the authentication cookie regardless of whether it is temporary or permanent.

It's also possible to supply forms-based authentication services with a list of valid credentials using configuration, as shown in the following example:

 <authentication> <credentialspasswordFormat="SHA1" > <username="Mary" password="GASDFSA9823598ASDBAD"/> <username="John" password="ZASDFADSFASD23483142"/> </credentials> </authentication> 

The application can then call FormsAuthentication.Authenticate , supplying the username and password, and ASP.NET will verify the credentials. Credentials can be stored in cleartext or as SHA1 or MD5 hashes, according to the value of the passwordFormat attribute. The possible values are listed in Table  9-5.

Table 9-5. Values of the passwordFormat Attribute

Value

Description

Clear

Passwords are stored in cleartext.

SHA1

Passwords are stored as SHA1 digests.

MD5

Passwords are stored as MD5 digests.

Authorizing Users and Roles

ASP.NET is used to control client access to URL resources. It is configurable for the HTTP method used to make the request ( GET or POST ) and can be configured to allow or deny access to groups of users or roles. To illustrate ASP.NET in action, consider an example in which you want to grant access to the user John and to the Admins role. All other users are denied access. The following example shows part of the XML code that needs to be included in Web.config:

 <authorization> <allowusers="john@microsoft.com" /> <allowroles="Admins" /> <denyusers="*" /> </authorization> 

Permissible elements for authorization directives are allow or deny . Each allow or deny element must contain a users or a roles attribute. You can specify multiple users or roles in a single element by providing a comma-separated list.

 <allowusers="John,Mary" /> 

You can indicate the HTTP method by using the Verb attribute:

 <allowVERB="POST" users="John,Mary" /> <denyVERB="POST" users="*" /> <allowVERB="GET" users="*" /> 

This example lets Mary and John POST to the protected resources, while allowing everyone else only to use GET .

Two special (reserved) usernames are similar to the Everyone and Anonymous accounts in Windows:

 *:Allusers ?:Anonymous(unauthenticated)users 

These special usernames are commonly used by applications that use forms-based authentication to deny access to unauthenticated users, as shown in the following example:

 <authorization> <denyusers="?" /> </authorization> 

URL authorization is computed hierarchically, and the rules used to determine access are as follows :

  • Rules relevant to the URL are collected from across the hierarchy, and a merged list of rules is constructed .

  • The most recent rules are placed at the head of the list. This means that configuration in the current directory is at the head of the list, followed by configuration in the immediate parent, and so on, up to the top-level file for the computer.

  • Rules are checked until a match is found. If the match is allowable , access is granted. If not, access is disallowed .

This means applications that are not interested in inheriting their configuration should explicitly configure all of the possibilities relevant to them.

The default top-level Web.config file for a given computer allows access to all users. Unless an application is configured to the contrary (and assuming that a user is authenticated and passes the file authorization ACL check), access is granted.

When roles are checked, URL authorization effectively marches down the list of configured roles and does something that looks like the following pseudocode:

 IfUser.IsInRole("ConfiguredRole")Then ApplyRule() EndIf 

What this means for your application is that you use your own class that implements System.Security.Principal.IPrincipal to provide your own role-mapping semantics (as explained earlier).

I l @ ve RuBoard


Designing Enterprise Applications with Microsoft Visual Basic .NET
Designing Enterprise Applications with Microsoft Visual Basic .NET (Pro-Developer)
ISBN: 073561721X
EAN: 2147483647
Year: 2002
Pages: 103

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net