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

Visual CSharp .NET Developer's Handbook (2002) [eng]

.pdf
Скачиваний:
35
Добавлен:
16.08.2013
Размер:
4.94 Mб
Скачать

know the change took place unless you actually check the entry. Figure 8.9 shows the LDAP output for the sample application.

Figure 8.9: The LDAP output is more complete than the WinNT output, but requires more work as well.

Where Do You Go From Here?

This chapter has shown you some of the intricacies of working with Active Directory in a managed C# environment. You've learned the effects of various path configurations and some of the tools you can use to research those effects. The example application also showed you how to work with values that CLR doesn't understand.

One of the best ways to learn about Active Directory is to spend time exploring it. The ADSI Viewer utility provides everything you need to perform queries and learn about the various objects. This tool also helps you modify the Active Directory schema to meet specific company needs. Because you need to rely on COM, in some cases you'll also want to view the interfaces and classes imported from the ACTIVEDS.TLB file. Finally, it's important to spend time using the online resources mentioned in the chapter.

Chapter 9 will help you understand security under Windows and the .NET Framework. Security has taken on increased importance because of the viruses and cracker break-ins that many companies experience. This chapter will show how new .NET features enable you to create a secure environment for the applications you create. Especially important is the use of role-based security to ensure that everyone gets the correct level of access to the application, but nothing more.

Chapter 9: Designing with Security in Mind

Overview

At one time, PC applications resided on a local machine or a LAN, so security requirements were small or non-existent. As developers created larger applications that affected critical enterprise data, the need for security increased. Today, applications commonly service the needs of distributed environments where employees and partners log in from remote locations to manipulate critical enterprise data. The only problem is that the security provided with many applications is still at the stand-alone machine or LAN level. Adding to the problem of

application security are operating system bugs, administration errors, and the forays of crackers.

Note For the purposes of this book, the term cracker will always refer to an individual who's breaking into a system on an unauthorized basis. This includes any form of illegal activity on the system. On the other hand, a hacker will refer to someone who performs authorized (legal) low-level system activities, including testing system security. In some cases, you need to employ the services of a good hacker to test the security measures you have in place or suffer the consequences of a break-in. This book will use the term hacker to refer to someone who performs these legal forms of service.

The role for developers in managing security has changed over the years as well. A developer is now part of the security team, and management often demands that the developer provide security features as part of application design. The security emphasis, for most developers, is to prevent security breaches from occurring, provide monitoring to warn when a security breach does occur, educate in the form of warning messages and online help, and improve security through the use of new technology.

Note Lest you think that .NET is impervious to virus attack, crackers have created a "proof of concept" virus named W32/Donut. The virus doesn't do any severe damage, but it does demonstrate .NET application vulnerability. Read more about this virus at http://www.infoworld.com/articles/hn/xml/02/01/09/ 020109hndonut.xml?0110tham.

This chapter will help you learn about new technologies, such as role-based security, that reduce the burden of creating a secure application. We'll also discuss different techniques for implementing security. For example, you'll learn when built-in security is better than using a configuration program to implement security. Finally, we'll look at some areas crackers hope you'll ignore, such as security monitoring. In some cases, you can detect usage patterns that signal security problems your application can't prevent due to holes in the operating system or other factors.

Note This chapter doesn't provide concrete answers that are going to work in your specific situation. A single chapter can't cover all of the technologies available today or discuss every security issue you'll ever run into. What we'll concentrate on instead is the programmer's perspective on security matters. We'll talk about the Visual Studio .NET and Windows technologies that are available to you as a programmer, but we won't get into specific solutions to a specific problem. The solution you finally decide to use will have to be based on your company's needs and the tools you have at your disposal.

Choosing a Security Strategy

Crackers depend on a certain level of disorganization within a company in order to employ their trade. That's why it's important to create a security strategy for your organization before you even begin coding the application. New programming features in Visual Studio .NET rely on your ability to define the application user. For example, you can't develop a role-based security strategy for an application until you know which roles the users could fulfill as they use the application. Some developers brainstorm roles they think an organization will need, but this leaves holes from unused roles in the application implementation and makes the application more complicated to configure.

Note Microsoft has produced a plethora of Windows operating system versions. For the purposes of this discussion, we'll divide those versions into two categories. I'll refer to Windows 95/98/Me as Windows 9x throughout the chapter. Likewise, I'll refer to Windows NT/2000/XP as Windows 2000 throughout the chapter. If I need to refer to a specific operating system version so that I can point out a new or interesting feature, I'll refer to the operating system by name. We'll discuss the reason for this operating system division in the "Working with Windows Security" section of the chapter.

The developer also needs to decide how to implement security. You can configure security at the client, the server, or both. Security can appear as an internal programmed function, something the administrator can configure using an MMC snap-in or other tool, a usercontrolled application feature, or an add-on implemented as needed for distributed requirements. In many cases, an application will require several levels of security and use more than one method for implementing it. The application design should specify what type of security to implement and how to implement it. There are situations, such as data security, when the feature is an absolute and should appear as a hard-wired feature of the application. Of course, you might want to place the security feature within a DLL, so you can update it as crackers develop new ploys for gaining access to your system.

Another consideration is the type of security you want to implement. You have a choice of traditional Windows security, newer role-based security, or third-party implementations. Third-party implementations are important because Windows doesn't fill every gap in the modern application. For example, if you choose to use the SOAP or some other XMLderivative to transfer data, you'll need a third-party product to secure the information in most cases (using a secure server connection does help).

Most developers find that role-based security works best at the server. It enables the developer or network administrator to create a security policy based on the tasks the user must perform, rather than focusing on the user's job title or other personal information. Role-based security is extremely flexible because you can employ it programmatically or rely on the network administrator to configure it. A developer also has the option of enforcing a minimum security level and allowing the network administrator to enforce more stringent security measures as needed.

Standard Windows security is still the favorite on the client side, because it works with the local machine policies and security features. Windows standard security attaches an access control list (ACL) to each object. The user's security identifier (SID) acts as a key to access the object.

Working with Windows Security

This section of the chapter helps you understand standard Windows security—the type that relies on object-level access tokens. Many of the applications you create will work fine with this level of security. We'll begin with a look at the Windows Security API. Next, we'll explore a Windows security example that demonstrates Windows security features. You'll learn about Windows security programming pitfalls and how to use two security-related utilities. Finally, we'll discuss cryptography.

Understanding the Windows Security API

The Windows security API is vast and performs many functions within the operating system and the applications it supports. We're going to talk about two essential topics in this portion of the chapter.

The first section is an overview of the security API from a programmer's perspective. While the user may be faintly aware that there's a security API, they're unlikely to use it or even care that it exists. As a programmer, you need to be very aware of this part of Windows 2000 and know how to use the various API calls to make your applications secure.

Using the Biometrics API to Ease Security Concerns

One security API to consider relies on biometrics—the use of human body parts such as the iris and fingerprints for identification purposes. The Biometrics API (BAPI) helps programmers embed biometric technology into applications. A consortium of vendors including IBM, Compaq, IO Software, Microsoft, Sony, Toshiba, and Novell originated BAPI. Learn more about BAPI at the IO Software website (http://www.iosoftware.com/products/licensing/bapi/).

You can download an overview, general information, technical information, and the BAPI Software Development Kit (SDK). Lest you think that all of these APIs are vendor specific, you can also find a biometrics standards link at the Biometrics Consortium website (http://www.biometrics.org/). This site contains helpful information about seminars, standards progress, and public information such as periodicals. Another interesting place to look for information is the National Institute of Standards and Technology (http://www.itl.nist.gov/div895/isis/projects/ biometricsproject.html). The main interests at this site are the publications, conferences, products, and success stories.

The second section highlights the cryptography API. This API allows you to encrypt and decrypt messages under Windows 2000. It provides the built-in cryptography support used by many applications. Of course, you can also buy third-party alternatives.

Why Worry About the Windows API?

Some developers are under the misconception that the .NET Framework is a complete solution or that it will answer every need. The problem is that the .NET Framework is new technology that extends what developers used in the past—you can't count on it to answer many of the old problems you have. In many cases, you'll find that a particular level of functionality is completely missing. The example in the section demonstrates one such instance.

However, the problem isn't limited to just missing functionality. Hidden within the .NET Framework are problems where you could assume one level of functionality when the .NET Framework provides another. Consider the System.IO.FileStream.Lock() method. In theory

you should use this method to lock a file. In fact, it will lock the file if no one else is using it at the time.

Unfortunately, the Lock() method uses the LockFile() function found in KERNEL32.DLL, not the more functional LockFileEx() function. This means you don't have the option to ask Lock() to wait until it can lock the file—the method always returns immediately, even if it can't lock the file. In addition, you can't differentiate between a shared and an exclusive lock. Your only choices to get around this problem are to create a loop and continually poll the file until it locks, or use PInvoke to execute the LockFileEx() function. In short, the .NET Framework is incomplete, and you'll need to know how to work with the Windows API to overcome those limitations.

Windows Security API Overview

The Windows security API is large and cumbersome. Let's begin with the user end of the picture. It's important to understand that the user's access is limited to the combination of groups and individual rights that the administrator assigns. However, most of the configuration options available to the administrator affect Windows as a whole. If you want the administrator to set user-level access for your application, then you must provide a feature to set user access for each object or task that your application provides.

User-level access depends on a security ID (SID). When the user first logs into the system, Windows assigns an access token to the user and places the user's SID (stored on the domain controller or other security database) within it. The user object carries around both the access token and the SID for the duration of the session. An access token also contains both a DACL and an SACL. The combination of ACLs and SID within the access token is a key that allows the user access to certain system resources. Table 9.1 contains a list of the various API functions that you'll commonly use to change the user's access token. This list only provides an overview, and not a detailed description, of each API function.

Table 9.1: Common User Access Token Function Overview

Function Name

 

Description

AdjustTokenGroups

Allows you to adjust one or more group flags that control group usage within the access token. For example, you can use this function to replace the group's owner.

AdjustTokenPrivileges

Allows you to adjust one or more privileges within the access token. This function enables or disables an existing privilege; you can't add or delete privileges from the access token.

AllocateLocallyUniqueId

BuildExplicitAccessWithName

Creates a new LUID. The LUID is only unique for the current computer session on a particular computer. Unlike a GUID, an LUID is temporary.

Creates an EXPLICIT_ACCESS data structure for the named trustee. This data structure defines the trustee's

Table 9.1: Common User Access Token Function Overview

Function Name

 

Description

BuildTrusteeWithName

BuildTrusteeWithSid

CheckTokenMembership

CreateRestrictedToken

DuplicateToken

DuplicateTokenEx

GetAuditedPermissionsFromAcl

GetEffectiveRightsFromAcl

GetExplicitEntriesFromAcl

GetTokenInformation

ACL information. Use this data structure with API functions like SetEntriesInAcl to define a trustee's access level to objects. The EXPLICIT_ACCESS data structure can affect either the SACL or DACL, depending on the access mode you set for it.

Creates a TRUSTEE data structure used to identify a specific trustee. You supply a trustee name, and Windows fills the other data structure elements with default values. You'll need to modify the data structure before using it.

Creates a TRUSTEE data structure that relies on a SID, rather than a trustee name. Windows modifies the default data structure values appropriately.

Determines whether a SID appears within an access token. This can help you to determine if a user or process belongs to a particular group.

Creates a duplicate of an existing token. The new token will have only a subset of the rights within the existing token. You can't use this function to add new rights to the resulting token.

Creates a copy of an existing token. Using this technique allows you to create a new token that varies from an existing token by one or two privileges.

Creates a duplicate of a token. This function allows you to create either a primary or impersonation token. You can set access rights to the new token as part of the duplication call.

Returns a list of ACL entries that result in an audit log entry for the specified trustee. This includes ACL entries that affect the trustee as well as groups to which the trustee belongs. You get a complete list of all audit-generating access events, not just those associated with the trustee. Windows returns the audited access in an ACCESS_MASK data structure.

Returns a list of ACL entries that list the effective rights for the specified trustee. Windows returns the effective rights in an ACCESS_MASK data structure.

Returns an array of EXPLICIT_ACCESS data structures that define the level of access each ACE within an ACL grants the trustee. The data structure provides information like the access mode, access rights, and inheritance setting for each ACE.

Returns a data structure containing complete information about the access token. This includes the

Table 9.1: Common User Access Token Function Overview

Function Name

 

Description

GetTrusteeForm

token's user, groups that appear within the token, the owner of the token, the impersonation level, and statistics associated with the token.

Returns a constant from one of the TRUSTEE_FORM enumeration values for a trustee. In most cases, the constants indicate whether the trustee is a name, a SID, or an object.

GetTrusteeName

Returns the name associated with a name trustee. If the TRUSTEE data structure that you provide is for a SID or an object, Windows returns a NULL value.

GetTrusteeType

IsTokenRestricted

LookupPrivilegeDisplayName

LookupPrivilegeName

LookupPrivilegeValue

OpenProcessToken

OpenThreadToken

SetEntriesInAcl

SetThreadToken

Returns a constant from one of the TRUSTEE_TYPE enumeration values for a trustee. In most cases, the constants indicate whether the trustee is a user, group, domain, or an alias. There are also values to show deleted or invalid trustees.

Detects whether the access token contains one or more restricting SIDs.

Converts a privilege name listed in WINNT.H to human-readable form. For example, SE_REMOTE_SHUTDOWN_NAME might convert to "Force shutdown from a remote system."

Allows you to convert a privilege name specified by an LUID to one of the constant forms listed in WINNT.H.

Allows you to convert a privilege name as listed in WINNT.H to an LUID.

Opens a token associated with a process (application). Like a file, you need to specify the level of access to the token. For example, the TOKEN_ALL_ACCESS constant gives you complete access to the token.

Opens a token that's associated with a thread within an application. As with a process token, you need to request a specific level of access when making the request.

Creates a new ACL by merging new access control or audit control information into an existing ACL. You can use this function to create an entirely new ACL using the ACL creation function, BuildExplicitAccessWithName.

Used mainly to implement impersonation within a thread. Use this function to give different rights to a single thread within an application. This allows the thread to perform tasks that the user may not have the rights to perform.

Table 9.1: Common User Access Token Function Overview

Function Name

 

Description

 

 

 

SetTokenInformation

 

Sets the information contained within an access token.

 

 

Before you can set the information within the token,

 

 

you have to have the required access rights. The three

 

 

data structures associated with this function allow you

 

 

to adjust owner, primary group, and DACL

 

 

information.

Note The tables in this section don't provide a complete list of all of the functions in the security API. Functions were chosen because they're unique, new, modified, or representative of a larger group of functions.

Normally, you'll never work with SIDs directly, because you can address a user by their login name and make your code easier to both debug and understand. However, there are certain situations when you'll want to work with SIDs. The most important of these situations is when you're dealing with common SIDs like the one for the World, which has a SID of S-1-1-0. The SID for the World always remains the same, but the name for the World could change from country to country. Always refer to common, universal SIDs by their SID rather than their common name. With this in mind, you'll want to know about SID-related functions for times you want to work with common SIDs. Table 9.2 contains a list of SID-related functions.

Table 9.2: Common SID-Related Function Overview

Function Name

 

Description

 

 

 

AllocateAndInitializeSid

 

Creates and initializes a SID with up to eight

 

 

subauthorities.

ConvertSidToStringSid

Converts a SID to a string in human-readable format. This format consists of values in the form S-R-I-SA, where S designates the string as a SID, R is the revision level, I is the identifier authority value, and SA is one or more subauthority values. Note that the dashes between SID values are always part of the SID string.

ConvertStringSidToSid

 

Converts a specially formatted string into a SID.

 

 

 

CopySid

 

Creates a duplicate of an existing SID.

 

 

 

EqualPrefixSid

 

Compares two SID prefixes for equality. A SID

 

 

prefix is the SID value minus the last subauthority

 

 

value. This test is useful for detecting two SIDs in the

 

 

same domain.

 

 

 

EqualSid

 

Compares two SIDs for equality in their entirety.

 

 

 

FreeSid

 

De-allocates the memory used by a SID previously

 

 

created using the AllocateAndInitializeSid function.

 

 

 

GetLengthSid

 

Returns the length of a SID in bytes.

 

 

 

GetSidIdentifierAuthority

 

Returns a pointer to a

 

 

SID_IDENTIFIER_AUTHORITY data structure that

 

 

contains an array of six bytes that specify the SID's

 

Table 9.2: Common SID-Related Function Overview

Function Name

 

 

Description

GetSidLengthRequired

top-level authority. Predefined authorities include NULL (0), local (1), world (2), creator (3), and Windows NT/Windows 2000 (5).

Returns the length of a buffer required to hold a SID structure with a specified number of subauthorities.

GetSidSubAuthority

GetSidSubAuthorityCount

InitializeSid

IsValidSid

LookupAccountName

LookupAccountSid

Returns the address of a specific subauthority within a SID structure. The subauthority is a relative identifier (RID).

Returns the address of a field used to hold the number of subauthorities within the SID. Use this address to determine the number of subauthorities within the SID.

Sets the identifier authority of a SID structure to a known value using a SID_IDENTIFIER_AUTHORITY data structure. Subauthority values aren't set using this function. Use the AllocateAndInitializeSid function to initialize a SID completely.

Determines the validity of a SID structure's contents. This function checks the revision number and ensures that the number of subauthorities doesn't exceed the maximum value.

Retrieves the SID (and accompanying data) for a specific account. You must supply an account and system name.

Retrieves the name and machine associated with a given SID. It also returns the name of the SID's first domain.

Security isn't this one-sided. Once Windows determines the rights a user or other object has, it must match those rights to the access requirements of the system resource. This means working with security descriptors. A security descriptor is a lock on the object or other system resource. The key (access token) fits the lock or it doesn't. Windows only grants access when the key fits the lock. Table 9.3 is an overview of the security descriptor API functions.

 

Table 9.3: Security Descriptor Function Overview

Function Name

 

 

Description

ConvertSecurityDescriptorToStringSecurityDescriptor Converts a security descriptor to string format. Flags determine the level of information returned in the string. A complete string contains the owner SID, the group SID, a DACL flag list using coded letters, a SACL flag list using coded letters, and a

Table 9.3: Security Descriptor Function Overview

Function Name

 

Description

 

 

 

 

 

series of ACE entries.

ConvertStringSecurityDescriptorToSecurityDescriptor

 

Converts a specially formatted string

 

 

into a security descriptor.

 

 

 

GetNamedSecurityInfo

 

Returns the security descriptor for the

 

 

named object provided as input. Flags

 

 

determine what kind of information to

 

 

retrieve.

GetSecurityDescriptorControl

Returns the security descriptor control information and revision number for the security descriptor structure provided as input.

GetSecurityInfo

Returns the security descriptor for an object that is specified using an object handle. Windows 2000 provides flags that determine which security descriptor entries to retrieve.

SetNamedSecurityInfo

Modifies the security descriptor information for an object specified by name.

SetSecurityDescriptorControl

Modifies the control bits of a security descriptor. Functions related to this one include SetSecurityDescriptorDacl, which allows you to set other control bits of the security descriptor.

SetSecurityInfo

Modifies the owner, group, SACL, or DACL within the security descriptor for an object. Each information type requires a separate data structure. Flags to tell Windows 2000 which elements to change. A handle and object type descriptor identifies the object.

By now, you should have some idea of how to work within the security API Windows 2000 provides. The divisions I set up within the tables are artificial; they've been used for description purposes to make the functions easier to comprehend and use. In a real-world application, you'll combine elements of all three tables to create a complete security picture.

Cryptography API Overview

Sometimes data protection is more a matter of adding hurdles to discourage theft than preventing the theft from occurring. Encrypting data at several levels adds "doors" the cracker must pass through to get to the data. Put enough doors between your data and the cracker, and the cracker will find something easier to break. (Of course, you should always assume that