|"One of the biggest design issues in COM+ component security is where
to implement security.
The trend in software development today is toward distributed applications, with components running on every platform across the network. One of Microsoft’s primary goals for Windows 2000 was heavy support for n-tier development, and it beefed up operating system support for components in the form of COM+. With its component services, Windows 2000 is a dream environment for software developers.
But security issues are vitally important for modern applications, and distributing code to multiple machines across the network presents the potential for security nightmares. When you encapsulate code in many small components-such as in a front end user interface, stored procedures on the data server, and God knows where else-the potential for security breeches multiplies rapidly. Rather than protecting a monolithic desktop application, you have to design and secure many components on many machines.
Recognizing this, Microsoft endowed COM+ with a set of security features that provide plenty of power and flexibility to secure an application’s components. Developers can team with system administrators to implement security both from the inside out (programmatically) and the outside in (administratively). These are valuable tools in the hands of those who know how to put them to good use, but can be the blueprint for disaster in the hands of the inexperienced.
In this article I’ll discuss the four primary elements of component security in COM+: declarative role-based security, programmatic role-based security, authentication services, and impersonation services.
Declarative Role-Based Security
COM+ provides a number of component security features right out of the box, so that you don’t have to build any security features into your applications. Just deploy them to a Windows 2000 server, set some permissions in the Component Services MMC (Microsoft Management Console) snap-in, and security is in effect for these components.
This simplicity takes advantage of declarative role-based security and the authentication services in COM+. Role-based security is similar to the groups in Windows NT and 2000. You can define a set of roles that mimic the roles that users or applications take on within an organization, assign permissions for various actions to each component and even its individual methods. This gives you a fine granularity of control over the resources used by an application or user.
Declarative role-based security is implemented within a COM+ application-called a package in Microsoft Transaction Server under Windows NT-defined within Component Services. Once a component is installed into a COM+ application, you can select what roles have permissions for each component, shown in
The advantage of declarative security is that you can apply it to a component that is built and compiled, without changing any of the programming code. You set these permissions using either the Component Services snap-in or programming the associated administration object model. So if security access control to the level of individual methods is sufficient, any component running on Windows 2000 and installed as a COM+ application can take advantage of these services. Best of all, changing a security policy doesn’t require recompiling or redeploying the components.
Programmatic Role-Based Security
Declarative security can provide an ideal granularity for component security, particularly when the components have not been designed with security in mind. But if the application does not lend itself to such generalized security at the component or interface levels, you can build role-based security programmatically. This is useful for situations where access is not an all or nothing decision. Some users—Managers, for example—may be allowed to give any discount to the very best customers, but others are limited to granting 15 percent. This kind of business rule requires different execution paths based on a user’s authority.
Most of the security services in COM+ are available either as COM interfaces or through ActiveX components. The latter is exposed through the COM+ Services Type Library, contained within COMSvcs.dll. The SecurityCallContext object lets you check role membership, access security information, and determine whether security is enabled for the current method invocation. A portion of this object library is shown in
, displayed in the Visual Basic 6.0 object browser.
The IsCallerInRole method returns True if the caller’s user ID is included in the specified role. This lets you test for role membership and follow different execution paths based on the return value:
Another task is determining whether role-based security is enabled for the current object. The IsSecurityEnabled property returns True if it is enabled for the application. You should use this property before calling IsCallerInRole, because the latter returns true if security is not enabled. At first thought this seems like a bug, but it makes sense to allow processing to proceed if no security is implemented.
Before COM+ can authorize a client to access resources, it has to be sure that it is who it says it is, the job of authentication services. Implementing role-based security is an effective method of securing an application and resources, but only if you know that the user is who she says she is.
COM+ server applications have several authentication options, for varying levels of security. The options range from no authentication to encrypting all packets and method parameters. Not surprisingly, the higher the level of security, the bigger the performance hit, as COM does more work and slows throughput. Figure 2 displays the security tab of a COM+ application's properties page.
: Activating authentication checks for a COM+ application is as simple as right-clicking the application in the Component Service snap-in, and checking the 'Enforce access checks for this application' checkbox. 'Authentication levels for calls' selects the level of security for the application's components.
The client and server components and applications participate in negotiating the authentication. Each expresses its preference for the level of authentication, and they select an appropriate service.
Sometimes a server application needs to take on the identity of a client application in order to gain access to resources it needs to perform its duties. The server is acting on the client's behalf, impersonating it for the purposes of the call. In essence, this allows the server process to execute in a security context different from its own.
Impersonation requires participation by both the client and server. The client must indicate its willingness to let the server use its identity; and the server must assume the client's identity programmatically.
While impersonation is a valuable and necessary technique in many situations, it can affect the performance and scalability of an application. Because of the overhead involved, it is usually slower to impersonate a client on a call than to make the call directly. Microsoft indicates that there are three issues affecting performance:
- The computational overhead of passing around identity in complicated patterns
- The general complexity of enforcing redundant security checking in numerous places, instead of just in the middle tier
- Resources such as database connections, when opened impersonating a client, cannot be reused across multiple clients
One limitation of impersonation is that it can't be used with queued components, which allow a client application to invoke a method on a server component that is not currently available. This makes some sense, because when a client makes a call to a queued object, the call is actually made to the Recorder object, which packages it as part of a message to the server. The Listener then reads the message from the queue and passes it to the player. By the time the server receives the call, the original client security token is unavailable. But role-based and programmatic security will still work.
Where Should I Implement Security?
One of the biggest design issues in COM+ component security is where to implement security. It can happen at any tier of the application: the database, the middle tier, client application, or any subset or combination of tiers. The decision is made all the harder by the number and variety of security features, and the number of potential client paths through which the application can wend its way.
Microsoft's recommendation is that you implement security on the middle tier whenever possible, where business rules and data transformations between the client and database tiers are typically implemented. By following this advice, you can take full advantage of COM+'s security features, and avoid the hits to performance and scalability when security is primarily implemented at the database. Security in the database is a tempting location, however, because data is widely viewed as a valuable asset.
When implementing security in the middle tier, you secure database objects so that the middle tier components access them under their own identity, such as by using an application role in SQL Server 7.0. Then the database need only authenticate the COM+ application, trusting it to do its own authentication of the client application. The biggest advantage of doing security this way is that database connection pooling becomes possible, since all data is accessed through a single identity.
The good news about COM+ component security is that it gives you a great deal of detailed control over the security of the individual components in your application, control necessary to implement distributed n-tier applications. The bad news is that there are so many options that it is all too easy to create a complicated monster full of security holes. Choose your implementation wisely, and you'll build a successful application. At least in terms of security.
Don Kiely is a Software Technologist at Third Sector Technologies in Fairbanks, Alaska, and has written and co-written several programming books.