Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional ASP.NET Security - Jeff Ferguson

.pdf
Скачиваний:
28
Добавлен:
24.05.2014
Размер:
13.26 Mб
Скачать

Implementing Authorization

This example application contains five web pages. Basically, it contains an Index Page, and pages for users to create, view, update, and delete their profiles. Permissions are defined for two roles: a role called Client and another role called Manager. Any user who belongs to the Client role can access the view and update profile information pages. Create and delete privileges are defined only for the users who belong to the Manager role. We can implementation this CustomAuthorization behavior in the following way.

Creating Users and Roles

All users for this application will be Windows users, and all roles are Windows Groups. The Client role is created as a Windows Group, and the users Alice and Bob are added to the Client Group. The Manager role contains the users Tom.

Creation of Custom HttpModule.

We have already explained the use and implementation of IHttpModule. Here we will create a custom HttpModule, which implements IhttpModule, and will implement the logic to validate the role with the requested page. If the user belongs to the Client role and the requested page is one of either Index. aspx , ViewProf lie .aspx, or UPdateProf ile.aspx, then access is granted. On the other hand, if the user belongs to the Manager role, the user is allowed to access any information.

To implement this logic, we'll create a DLL, which we'll call CustomAuthorizationModule . dll. We'll create a new C# component project, which we'll call CustomAuthorizationModule, and define a class called CustomAuthorizationModule.

As IHttpModule is a part of System. Web namespace, we need to reference System.Web.dll.

Now we implement IhttpModule in the CustomAuthorizationModule class, and the actual authorization logic code.

The following code implements the logic for CustomAuthorizationModule.

using System; using System. Web;

using System. Threading; using System. Security;

using System. Security . Principal ; using System. Security . Permissions ; namespace CustomAuthorizationModule

{

public class CustomAuthorizationModule : IHttpModule

{

private static string[] AuthorizedPages = new string[5];

public CustomAuthorizationModule ()

public void Init (HttpApplication objHttpApplicationcontext)

{

objHttpApplicationcontext . AuthenticateRequest += new

EventHandler (this .CustomAuthorizationRequest) ;

I

public void Dispose))

private void

CustomAuthorizationRequest(object Sender,EventArgs evtEventArguments)

HttpApplication objHttpApplication = (HttpApplication) Sender; if

( objHttpApplication.Context.User.IsInRolef"RAM\\Client") )

AuthorizedPages[0]="/WroxSample/WroxAuthorization/Index.aspx";

AuthorizedPages[1]="/WroxSample/WroxAuthorization/ViewProfile.aspx";

AuthorizedPages[2]="/WroxSample/WroxAuthorization/UpdateProfile.aspx";

if ( objHttpApplication.Context.User.IsInRolef"RAM\\ ") )

AuthorizedPages [0] = " /WroxSample/WroxAuthorizat ion/Index, aspx" ; AuthorizedPages [1] = " /WroxSample/WroxAuthorization/CreateProf ile.aspx" ; AuthorizedPages[2]="/WroxSample/WroxAuthorization/ViewProfile.aspx"; AuthorizedPages[3]="/WroxSample/WroxAuthorization/UpdateProfile.aspx"; AuthorizedPages[4]="/WroxSample/WroxAuthorization/DeleteProfile.aspx";

try

{

bool PageAuthorized = false; int NoOf Pages = 0 ;

for (NoOf Pages=0;NoOf Pages<Author izedPages . Length ;NoOf Pages++)

{

if ( objHttpApplication. Context .Request .FilePath == AuthorizedPages [NoOf Pages] ) {

PageAuthorized = true; } else

{

PageAuthorized = false; } if (PageAuthorized)

break; }

if (PageAuthorized == false)

throw new SecurityException( "Authorizationf ailed" ); } catch (SecurityException se) { ob jHttpAppli cat

ion. Context . AddError (se) ;

Let's quickly go through what is happening in this CustomAuthorizationModule.

304

Implementing Authorization

When the Init method of CustomAuthorizationModule is called, it adds the CustomAuthorizeRequest method to the HttpApplication's AuthorizeRequest event. Now, whenever a user requests any page, Author izeRequest on HttpApplication gets triggered and in turn CustomAuthorizeRequest is executed.

Our logic for validating the user's role with the predefined ASP.NET pages are written in CustomAuthorizeRequest code.

When the access is violated, a security exception is thrown.

Creation of WroxAuthorizatlonSample Application.

Now that we have defined our CustomAuthorizationModule, let's develop our ASP.NET application.

We create five new Web Forms, and call them Index. aspx, ViewProf ile. aspx,

DeleteProf ile . aspx, UpdateProf ile. aspx, and CreateProf ile . aspx (all the source code for these files is available with the code download - our job here is to control access to these pages).

The Index.aspx page contains links to other web pages. Depending on whether the user's role is Client or Manager, access is granted to the appropriate pages.

To restrict only the Windows Users to log on to the application, configure the Virtual Directory Permissions to deny anonymous access.

Attaching CustomAuthroizationModule with ASP.NET Application

The Custom Authorization will take place only after we've attached the HttpModule to the ASP.NET application. In other words, we need to configure the web.config file and add the httpModules tag.

The following code shows how the web. conf ig is configured.

<httpModules>

<add type="CustomAuthorizationModule.CustomAuthorizationModule, CustomAuthorizationModule" name="CustomAuthorizationModule">

</add>

</httpModules>

CustomErrors

When you want to redirect the request to a CustomErrorMessage. aspx page upon failed authorization, we need to configure the web. conf ig to handle custom errors. The following code explains what to add in the web. conf ig to handle the custom errors.

<customErrors defaultRedirect="CustomErrorMessage.aspx" mode="On"/>

WroxAuthorization Application

Now that we have added these bits and pieces, we can run this application from the browser and see how Custom Authorization works.

When you log in to your computer as Bob or Alice, you should only be able to see View and Update Profile pages. But when you log in as Tom you should have access to all the pages.

Summary

Authorization provides an effective way to control access to resources. To implement authorization, we could use one of the .NET Framework's built in authorization mechanisms, such as File Authorization or URL Authorization. We have seen how to configure our ASP.NET application to work with both File Authorization and URL Authorization.

The .NET Framework provides us with the flexibility you to write our own custom authorization routine, and plug this routine into the ASP.NET application framework. Once we've explored in detail how the HttpModule and HttpApplication objects function, we are able to write our own custom authorization modules, and can integrate these with the ASP.NET Framework.

In addition to handling authorization to resources such as ASP Pages, MSMQ, FileSystem, and so on, we have also learned how to check for permissions at run time to control access at the method level. With the understanding of ASP.NET authorization framework and the HttpModules, we can implement highly customized authorization mechanisms.

306

Code Access Security

Throughout the previous chapters, we've examined how the .NET Framework implements different security models, such as Windows or Passport authentication. In these models, the security authorization infrastructure, which permits or denies access to certain resources or actions, is entirely dependent on the credentials the user (also known as the principal) provided in the logon phase.

Contrast with this Code Access Security (from now on referred to as CAS). This is, at first sight, something totally new for most developers and administrators. CAS provides a means to assign a sort of identity - called an evidence - to .NET assemblies, and such evidences will be evaluated against security policies at load time and run time in order to enforce access control authorization. User identities are completely out of the picture, so that the code that is running may be denied access to a resource even if the user identity attached to the process is allowed access. Obviously, CAS doesn't bypass standard user identity-based security mechanisms; as part of the .NET Framework, CAS is a layer sitting on top of them, so that access to a resource might be blocked, for instance, by NTFS permissions, even if access had been granted by CAS.

Authenticode was Microsoft's first attempt to apply some kind of authentication against the code itself. Unfortunately, this provided an all or nothing access control mechanism. According to the Authenticode publisher signature, which would be embedded into an ActiveX control, the end user has to accept or refuse to have the control installed on a system. If the user accepts, the component will be allowed to do whatever the permissions of the user allow them to, regardless of the fact that it is a very different type of thing from other types of applications, which will have been deployed on the system in a more controlled way.

The widely publicized security breaches of high profile internet applications draw attention to just how important internet application security has become, and application development technologies now have to implement various types of robust security technologies.

Although security was introduced in Java as an afterthought, Java had a huge advantage when compared to COMbased solutions: the Java Virtual Machine (JVM) has total control on the executing code. Via a sandbox model, Java code can be much more easily constrained and limited. It's much safer to run a Java applet within your browser than an ActiveX control.

Fortunately .NET was designed from the ground up with security in mind. CAS provides an ideal solution to the security issues that arise in Internet-based applications where code is downloaded from remote locations. As we will see, CAS is a robust but flexible security system, which enables us to customize the environment where partially trusted code runs.

By the end of this chapter, we will have investigated:

Q The differences and similarities between standard security and CAS

Q CAS authentication: evidences

Q CAS permissions

Q The CAS permission granting process: policies

Q Assembly security requests

Q Code security demands

Q Use of the CAS administrative tools.

As Code Access Security is quite distinct from the other aspects of ASP.NET security, which we have been looking at elsewhere in the book, we will be covering a lot of ground at quite a pace, introducing many new concepts. The first part of the chapter is dedicated to sketching the general structure of CAS, and later parts, to more detailed and practical investigations of implementing various aspects of CAS.

An Overview of Code Access Security

From a great distance, CAS may be seen as just another security model in which we encounter the familiar concepts of authentication and authorization. What makes it quite different from what we're used to dealing with lies in the types of actors involved in the authentication and permission granting process.

In the authentication context CAS doesn't deal with standard Windows identities (also known as principals) but with assembly evidences. Evidences bring information such as where the assembly was loaded from (for example, from the local hard disk or from the Internet) or its publisher signature (if there is one). Assembly evidences are evaluated and attached to the assembly when it is loaded into an application domain by the .NET runtime.

In the authorization context, CAS permissions are not related to OS-level objects. They are higher-level entities with a more natural and direct mapping to standard application tasks, such as accessing a database via an OLEDB provider or a resource on the Internet via a URL, thus helping the implementation of a robust Code Access Security policy. Moreover, providing a level of abstraction above OS-specific resources will make future porting of the .NET runtime to other Operating Systems significantly simpler. This is not an insignificant aspect of CAS, given the purported aim of the .NET Framework to aspire towards platform independence.

310

Code Access Security

That said, be prepared to become familiar with a totally new approach when we examine how CAS implements the permission granting process to assemblies, and how CAS checks them at run time.

The main difference lies in the fact that whereas CAS permissions are directly attached to assemblies at load time, standard security models pass user identities along the execution flow from one assembly to another (and even across processes if impersonation is turned on). An Access Control List (ACL) attached to the resource to be protected contains information about who can do what to the resource, as illustrated in the following figure:

Permissions provided at load time

Permissions

CAS

 

CLR checks if Assembly A has

 

 

 

 

attached required permission

to perform the operation

Standard Security model

Userl

x

Assembly

Try to access a resouce

OS Checks in the ACL if user has the permission

Note that this figure doesn't provide any detail about the CAS permission granting process, nor how and when access control checks are executed. We will of course examine this in detail later in the chapter. What we need to focus our attention on here is the difference between having a permission set attached and having an identity attached.

Can I Ignore CAS?

One may wonder to what degree CAS can be ignored when developing standard applications such as client-server applications. Well, generally, it's fair to say that we will not be able to ignore CAS. We can see this with a quick example.

Here is a simple console application that creates a text file on the C root directory.

namespace simpleclass

public class myclass

static void Main()

StreamWriter sw = new StreamWriter {"C:\\myflie.txt") sw.WriteLine ("a line"); sw.Close();

Build this application, then, instead of accessing it in the standard way, create a network share in your machine pointing to the directory where the EXE is located. Then launch the application through the shared network name. The command line should look something like \\<computername>\myshare\simpleclass.exe.

The result is not exactly the one we might have expected:

Microsoft CLR Debugger

An unhandled exception of type 'System Security SecurftyExtepfcrofV occurred in mscortib.dil

Additional information; Request for the permission of type System.Security.Permissions.FitelOPer mission, mscorlib,

Version* 1.0.3300.0. Cuiture^neutral, PubfrcKeyToken«b77a5c561934eOS9 failed.

Break

Help

We've just seen CAS in action: the FileStream object used internally by the StreamWriter makes a Security Demand of type FilelOPermission to assemblies upon its call stack. In other words, the FileStream object asks the Common Language Runtime (CLR) to check that all assemblies along the call stack (in this case there is just one: the direct caller assembly) have the permission to access the file system. In our case, the StreamWriter constructor statement fails, since default CAS policy configuration does not grant File IO permissions to assemblies loaded into the CLR from a network share (that is, the Intranet Zone).

Note that we have introduced an important point here: access controls are not originated by the CLR itself (except for one important exception, which we will investigate later, which was introduced shortly before the .NET Framework was released).

Code Access Security Basics

In this section we will introduce the main concepts required to get a grip on how Code Access Security works. We will describe the whole authentication and authorization process, which starts at the points at which the run-time host loads the CLR engine and creates an Application Domain.

Acquiring Assembly identity

The first problem that arises in defining a security model for blocks of code is to provide a concept of identity on which to apply security checks. An assembly identity is defined by a set of evidences. An evidences is a collection of pairs of names and values, containing information describing various aspects of the assembly. Some of these assembly properties are fixed, such as its publisher or strong name (these indicate the author of the assembly). Others may vary, for example indicating the URL the assembly was loaded from. This information set is determined by the CLR while loading an assembly into an Application Domain.

312

Assigning Permissions to Assemblies

Code Access Security

 

313

Once the assembly evidences have been determined, the CLR passes them to the policy evaluator, which decides what permissions (actually, permission sets) can be granted to an assembly. The .NET Framework comes with a list of built-in permissions types, one for each resource exposed by the Base Class Library (BCL). These include OLEDB permission, MessageQueue permission, FilelO permission, and so on. Since programmers can develop assemblies that provide access to application-specific resources, the .NET Framework enables us to define custom permissions to protect them. We'll see how to do this later, when we look at permission objects in more detail.

The policy evaluator compares assembly evidences against three different hierarchical policy structures (Enterprise, Machine, and User) defined by the .NET Framework. As we will see, each policy structure comes with a default configuration, but administrators and developers can modify it according to specific application requirements. Simply put, policy structures define what permissions an assembly must be granted when owning a specific evidence value. We will discuss the details of this process later in this chapter. For the moment consider the policy evaluator as a black box that receives an assembly with its evidences as input, evaluates its evidences against the policy configuration, and outputs the assembly with a set of granted permissions attached to it.

The last step that takes place at assembly load time is the evaluation of assembly permission requests. Permission requests are embedded into an assembly using attributes. By placing a permission request, we can declare what permissions an assembly needs to be granted (generally, we would do this because the absence of the specified permission would make the assembly unusable). If CAS policies have not provided the assembly with the requested permissions, the CLR will stop loading the assembly and throw an exception. Additionally, the assembly can specify what permissions the assembly refuses to be granted. When the policy evaluator meets a RequestRefuse attribute on the assembly metadata, it removes the refused permission from the list of granted permissions, which was previously assigned to the assembly by security policies.

These processes may be illustrated in diagrammatic form in the following way:

Access Control

Once an assembly has been successfully loaded into an application, it starts running, receiving calls from assemblies in the call stack, and calling methods on other classes, which reside either in the same assembly, or in different assemblies.

During code execution, the role of CAS is to provide a means for assemblies that give access to sensitive resources to check that received calls are allowed. These requested checks, known as Security Demands, must be placed by assemblies (declaratively or via proper class-level or method-level attributes). Base class libraries come with proper security demands defined. This means that, in most cases, we are not required to place Security Demands in our application code, unless we have to protect an application defined resource of which the .NET runtime is unaware.

Obviously, checking only that the direct caller assembly has proper permissions for a specific action is not enough. If access control worked like that, CAS would be seriously compromised. A rogue component with low permissions could call a trusted assembly and ask it to perform some sensitive tasks on its behalf, as illustrated in the following diagram.

 

 

 

Rogue assembly

Trusted assembly with

FileStream BCL class

Loaded from Internet

>

providing access to the file

^^^^^ Full permissions

With low permission

system

 

 

 

 

 

In order to provide effective access control, when the CLR meets a Security Demand along the execution flow, it parses the assemblies call stack to check whether all the assemblies have the demanded permission. If this is not the case, the CLR raises an exception.

An assembly along the call stack having a demanded permission is given a chance to stop the CLR stack walking, forcing a premature positive outcome of the stack walk, regardless of the fact that upper assemblies might not be granted the demanded permission. This is called Asserting a permission. Conversely, an assembly may have the stack walk fail prematurely, even if all the assemblies up in the call stack have the requested permission granted. This action is called Denying a permission. Both Assert and Deny actions have no effect if the assembly is not granted the permission they are denying or asserting.

As a side note I'd like to mention that while managed code can access the evidence the CLR has attached to it, there is no way for an assembly to know what permissions it has been granted (even if an assembly calls Demand, Assert, or Deny on a permission that it doesn't own, no exception is raised when the call is placed).

The diagram shows how stack walks can prevent an un-trusted assembly accessing the file system, even if access is performed on behalf of a trusted assembly.

314

Соседние файлы в предмете Программирование