Secure and Deploy Business Solutions with Microsoft Visual Studio Tools for Office
Brian A. Randell and Ken Getz
This article assumes you're familiar with Visual Basic .NET or C#
Microsoft Visual Studio Tools for the Microsoft Office System is a new technology that brings the advanced features of Visual Studio .NET and the .NET Framework to applications built for Microsoft Office Word 2003 and Microsoft Office Excel 2003. Deploying solutions built with this technology requires that you understand how runtime security is enforced in managed applications and how to configure users' systems to run your solutions without introducing security holes.
To promote that understanding, this article will demonstrate how to establish trust, explain policy considerations and permissions, and explain what trusted code is all about. Secure assembly deployment is also covered in detail.
In our September 2003 MSDN
article, we showed how Microsoft® Visual Studio® Tools for the Microsoft Office System makes it easy to create document-centric solutions for Microsoft Excel 2003 and Word 2003 using managed code. As long as you started from scratch and let the New Project Wizard do the dirty work, things worked great. The minute you moved your document and assembly to a new directory or computer, though, things no longer worked. To understand why, you need to be familiar with Code Access Security (CAS) in the Microsoft .NET Framework. CAS allows you to take advantage of one of the most compelling features of Visual Studio Tools for Office, namely, the ability to have a document consume a shared assembly that can be updated without requiring deployment to each desktop. In addition, you'll need to know how to share your code with other developers. You will either need to be a member of the local Administrators group or the Power Users group to change runtime security policy.
In the previous article, we outlined a few reasons why developers should care about Visual Studio Tools for Office. Two are worth repeating and are the focus of this article. First, developers can take advantage of CAS that's built into the common language runtime (CLR). Unlike Visual Basic® for Applications macros, managed code simply won't run unless it's explicitly trusted. The default policy permits no assembly to run and protects users from viruses and other malicious code. Before users can load a document with managed code extensions, the administrator must explicitly grant full trust to the corresponding assembly or its location. Second, developers using Visual Studio Tools for Office can take advantage of no-touch deployment. Users can automatically receive updates to the app with no intervention from developers or administrators.
In our earlier article, it was unclear how Microsoft planned to distribute Visual Studio Tools for Office. In June 2003, Microsoft announced a new suite called Microsoft Visual Studio Tools for the Microsoft Office System, the core components of which are the templates and runtime for creating managed assemblies that interact with Excel 2003 and Word 2003 (see Visual Studio Tools for Office
for more information).
It's Good to Share
Even if you program by yourself, you often need to move a solution from one machine to another, or even from one directory to another. The first thing to be aware of is that for debugging purposes each project hardcodes information about where the host application (Excel 2003 or Word 2003) has been installed. The second much thornier issue involves security.
In order to follow along with the discussion in this article, start Visual Studio .NET 2003 and create a new Microsoft Office System project. For our testing, we created a Visual Basic .NET project using a new Excel 2003 workbook. Name your project VSTOXLVB. If you don't know how to create the project, refer to our September 2003 MSDN Magazine article.
At this point, you'll have a fresh project ready to go. If you press F5 from within Visual Studio, you'll get a compiled assembly that is automatically loaded when Excel 2003 loads your workbook. Shutting down Excel 2003 unloads the assembly and closes the hosting document. If you share this solution with another developer, they might need to modify a project setting. Once you're back in Visual Studio, you can locate this setting by right-clicking the project in the Solution Explorer window, selecting the Configuration Properties folder, and then the Debugging node. In the property sheet, you should see the "Start external program" option selected. In the textbox next to it, the full path to Excel 2003 on your computer will be listed. If you have Excel 2003 installed on your C: drive, but your co-worker has it installed on their D: drive, the option will have to be changed before the co-worker can debug the application locally. Now that Visual Studio .NET can find Excel 2003, you have to deal with the security implications.
Back on your development computer, use Windows® Explorer to copy the host document file and the newly compiled assembly to another directory on your computer. By default, the host document (VSTOXLVB.xls in this case) is located in the same directory as your Visual Studio .NET solution file. The assembly is stored in two locations, the bin folder and the VSTOXLVB_bin folder, which are both found below the solution folder. Copy both files to the same new directory. Open the VSTOXLVB.xls file directly from Windows Explorer. The first problem you'll encounter is that the loader can't find the assembly even though it's in the same directory as the Excel 2003 workbook. You'll receive an error similar to the dialog box that's displayed in Figure 1.
Figure 1 Can't Find Assembly
As we mentioned in the previous article, the host document has been modified so that it contains two helper properties: _AssemblyName0 and _AssemblyLocation0. The properties are used by the Visual Studio Tools for Office loader DLL, OTKLOADR.DLL, to locate the assembly. In Excel 2003, with the VSTOXLVB.xls document loaded, select File | Properties to display the Document Properties dialog box. On the Custom tab, change the _AssemblyLocation0 property to be a period, indicating that the loader should look within the current folder for the assembly. Save the document, close it, and reopen it in Excel 2003. This time, you should get a different error message, which says:
The current .NET security policy does not permit VSTOXLVB to run from the folder .\. Do not change the security policy in your computer. The .NET security policy is controlled by your administrator or the developer who wrote the custom macros. You can still edit and save the document. Contact your administrator or the author of this document for further assistance.
By default, the Visual Studio Tools for Office loader only loads assemblies that are explicitly given full trust, and by moving the assembly you've removed it from a trusted location. There are three levels of trust available from the Microsoft .NET Framework: full, partial, and untrusted. Unless specifically told otherwise, the Visual Studio Tools for Office loader considers all managed assemblies to be untrusted. In addition, it only loads assemblies that have been granted full trust. The Visual Studio Tools for Office loader does not support partial trust scenarios because interaction with the host (Excel 2003 or Word 2003) requires COM interoperability, thus the CLR cannot verify that all executed code is safe.
In order to grant trust, you'll need to investigate the tools provided by the .NET Framework that make it possible for you to create and manage security policies. You can do this in three different ways: from the command line using CASPOL.EXE, using GUI administrative tools, or programmatically. We used GUI tools.
Close Excel 2003 (and delete the copied versions of your document and assembly). Open the Microsoft .NET Framework 1.1 Configuration MMC snap-in. Among other things, this configuration tool allows you to manage the runtime security policy for managed code on your computer. Starting with the Runtime Security Policy node, expand the User node, then the Code Groups node, and finally the All_Code node. Under the All_Code group, find the Office_Projects node. You will see a unique entry for each unique document in every Excel 2003 or Word 2003 solution you've created so far. Figure 2 shows what a test computer looks like after creating the sample for this article.
Figure 2 Test Directory
These entries tell the loader what code is trusted and allow you to write, debug, and run your code on your development computer. The code only runs if it's loaded from the exact location you specified when you created the project. To make life easier, the Office Project Wizard modifies your local runtime security policy, adding a policy that grants your assembly the rights it needs in order to run. This step only occurs on development computers. To get things to work on your users' computers, or on another developer's computer for that matter, you'll need to modify the target computer's runtime security policy.
Policy, Evidence, Permissions, and Code Groups
The .NET runtime (the CLR) has a well-defined security policy in place for managed code. If you want to learn more about this subject, see Don Box's MSDN Magazine
article in the September 2002
issue. To review that discussion, the runtime security policy sets permissions and allows the CLR to make decisions about what code is permitted to do. Permissions grant access to resources (such as files, databases, and so on), and permit code to perform some action (such as calling COM components). Policies allow the CLR to be proactive in protecting your assets.
The CLR uses a variety of criteria, referred to as evidence, to make its permissions decisions. Evidence can be the URL from which the code is loaded, a cryptographic hash based upon the assembly itself, or information about the author of the code.
Whatever the evidence, the CLR needs a way to match it with a permission set—that is, a logical grouping of permissions. This matching is accomplished through code groups and membership conditions. A code group is a logical grouping of code that matches a specified membership condition, which is a stipulation that must be met in order for an assembly to be affiliated with a code group. Each code group is associated with a specific permission set.
The entire runtime security policy is divided into four policy levels: Enterprise, Machine, User, and AppDomain. All of them, except AppDomain, can be managed visually using the Microsoft .NET Framework 1.1 Configuration MMC snap-in, shown in Figure 2. Each policy level contains one or more code groups, nested in a hierarchy. At run time, the CLR evaluates all of the available evidence to identify which code groups the assembly is a member of. It then grants the assembly the permissions specified by membership.
Things get interesting when you start to investigate the manner in which Office 2003 documents that use managed code extensions incorporate these security features. By default, the Enterprise and User policy levels contain only one code group each: All_Code. The All_Code group has a membership condition of All Code with the FullTrust named permission set. All the interesting policies are defined at the Machine level, which provides a set of code groups restricting what code can do, based primarily upon Zone membership conditions. The zones used as membership conditions in the default code groups map to the zones used by Microsoft Internet Explorer: Internet, Local Intranet, Trusted sites, and Restricted sites. They're defined as Internet, Local Intranet, Trusted Sites, and Untrusted Sites respectively in the runtime security policy. In addition, Microsoft provides a My Computer zone for your local hard drives.
Policy levels are evaluated in conjunction with one another. Therefore, the permissions given to an assembly are based upon the intersection of the assembly's membership conditions. All of the policy levels must agree in terms of the rights they give. If the Machine policy level explicitly denies execution rights to code coming from the Internet, the Enterprise policy level cannot implicitly grant permission. If you examine your Machine policy level, you should see that all code running from the My Computer zone runs with full trust. But as you saw earlier, when you copied your .xls file and assembly to a new directory, it wouldn't run. Why not?
Although there are four policy levels (Enterprise, Machine, User, and AppDomain), the Visual Studio Tools for Office loader applies AppDomain policy at run time. For example, Visual Studio Tools for Office does not accept membership in the My Computer zone as trustworthy. Your assembly must provide some other evidence that matches a code group providing full trust. It turns out that there are many types of evidence which you select when you modify a policy. You can choose from the following options: All Code, Application Directory, Hash, Publisher, Site, Strong Name, URL, Zone, and Custom.
Because the Visual Studio Tools for Office loader does not accept zone evidence, you must use something else. Another way to think about it might be that if you want to rent a video from the local video store and pay with cash, your video membership card is good enough. But if you want to buy 20 DVDs and pay with a credit card, they require your driver's license. That's two types of evidence, each providing different levels of trust.
The first time you create a Visual Studio Tools for Office project on your computer, the wizard adds an Office_Projects code group with a membership condition of All Code to the User Policy level. Then, for each additional solution you create with the Microsoft Office Project Wizard, Visual Studio .NET defines a new code group under the Office_Projects code group. The new group is a hierarchical group with two levels. The first level uses the full path to your assembly, excluding the assembly name. The second code group uses the full path to your assembly including the assembly name (as you see in Figure 2).
Both code groups use the URL membership condition, allowing the code to run based on its location. The first level assigns the Execute permission set to the code group. The second level assigns the FullTrust permission set to the code group, which has a membership condition for the specific assembly you created when you compiled your project. This adjustment to your runtime security policy allows you to press F5 and run the project without having to modify the CAS policies yourself. It satisfies the Visual Studio Tools for Office loader in that it provides additional evidence—in this case, URL evidence verifying that your assembly is trusted to be loaded into an Office 2003 host.
You might wonder why there is security at two levels within the file system. After all, if you were required to give the assembly FullTrust rights in order to run, why does the policy allow other code to run within the same directory? There are two reasons. The first is so that satellite assemblies that might, for example, contain resources such as localized string tables can be loaded. Second, it lets you reference other assemblies within the same folder, so that they will at least load and execute. If the separate assemblies try to do anything that requires more than simple execution (such as accessing a file) and you haven't granted them permission via a code group, they will fail. This means that any code you want to use as part of a Visual Studio Tools for Office solution (except code with the Microsoft or ECMA strong name) must meet the membership condition of some code group (other than the built-in My_Computer_Zone) and that code group must give FullTrust rights.
At this point, perhaps you've concocted a simple solution to the problem of moving your code from one location to another: you could assign FullTrust rights using URL evidence to your entire C:\ drive and be finished with it once and for all! This would be a really, really bad idea. Imagine, for example, what would happen if you downloaded an assembly from a Web site that included some malicious code. Because your temporary Internet folder is likely on your C:\ drive somewhere, you've just granted that assembly the rights to do whatever evil task it was designed to do.
There are many ways to trust code, and the mechanism you choose on your development computer is more for convenience than anything else. When deploying apps, however, you should do what's best for your users. This requires you to select evidence that's more secure, such as strong naming or X.509 certificates.
When using certificates or key files, it is a best practice to keep your release versions secure. When it's time to publish code, there should be some out-of-band process where only trusted members of your organization can sign code. For development, you have two options. The first is to define a development strong name key file that you use on your computer. If you have a team working on a project, you only need one key file. The important point is that this key file is used to sign code only for testing and development. For release, you sign the code again using a different key file. The second option is to use delay signing. When an assembly is delay signed at build time, space is reserved in the portable executable (PE) file for the strong name signature, but the actual signing is deferred until some later stage.
You create key files using the .NET Framework Strong Name utility, sn.exe, which comes with the .NET Framework SDK and Visual Studio .NET. Continuing with the earlier example, open the Visual Studio .NET 2003 Command Prompt and change to a directory where you'd like to store the key file. For the example, we're going to put it in a folder named C:\TestKeys. Within that folder, type "sn -k vsto_dev.snk" and press Enter. You should note that all the switches used by the Strong Name utility are case sensitive.
Before you go any further, you should back up your security settings. Your Enterprise and Machine policy files are stored under your Windows Drive:\Windows Directory\Microsoft.NET\Framework\v1.1.4322\CONFIG directory in two XML files: enterprisesec.config and security.config. The user policy file is named security.config and is stored in a user-specific location. On our test computer running Windows XP Professional SP1 and logged on as Administrator, the User policy file is stored at C:\Documents and Settings\Administrator\Application Data\Microsoft\CLR Security Config\v1.1.4322. You should always remember to back up these three files before you start modifying your security configuration.
Once the files are backed up, return to the .NET Framework 1.1 Configuration snap-in and delete the code group that lists just the path to your solution. This will in turn delete the child code group for the assembly. Verify that you can no longer run or debug the solution. You should receive the same .NET security policy error message described earlier.
How can you run the application without the URL-based policy? You'll now add support for using a strong name as evidence for your assembly instead. Return to Visual Studio .NET and open the AssemblyInfo.vb file. Add the following AssemblyKeyFile attribute below the version number, so that when your assembly is compiled, it has a strong name:
' Visual Basic
We've included the C# code for completeness. If you're creating a C# solution, the AssemblyInfo.cs file already contains the attribute; you just need to fill in the path.
Rebuild your solution and return to the .NET Framework 1.1 Configuration MMC snap-in. Generally speaking, you want to define your policies at the computer level. This lets you, as the local Administrator, set policy that can then be tested using a non-administrator account.
Starting with the Runtime Security Policy node, expand the Machine node, the Code Groups node, and then the All_Code node. Under the All_Code group, find the My_Computer_Zone node. Right-click on the My_Computer_Zone and select New to start the Create Code Group Wizard.
In the Name field, type "VSTO_Dev". Fill in the Description field, if you are so inclined, and click Next. On the Choose a Condition Type page, choose Strong Name from the Condition type combobox. Choosing the Strong Name option changes the page so that you can import the public key from your compiled assembly. Click Import, and using the displayed file dialog, navigate to the subdirectory containing your signed assembly. Select it and click OK.
Although you have the option on this page to trust any version of this specific assembly, or only a specific version, you want any code that is signed with your test key to be trusted, so click Next. On the Assign a Permission to the Code Group page, verify that FullTrust is selected in the Use existing permission set combobox and click Next and then Finish on the last page. At this point, you should open your Excel 2003 document and verify that it opens without error.
Because you've assigned a strong name to your assembly and have created a code access policy allowing any assembly using that strong name to run, you can now move projects anywhere on your local computer and not have to worry about these security issues. If you create a new project, you need to sign it with the same key file used to create your policy. Naturally, you can have code groups representing different strong names.
Figure 3 Turning Off the Default Security Option
Here is one last development note: when you create new projects, you can select the Security Settings option and uncheck the option to update your local security policy, so that the wizard doesn't set up a URL-based policy allowing your project to run automatically (see Figure 3). You can add the AssemblyKeyFile attribute yourself, in that case. If you're working in a team environment, it's less painful if you all use the same key file, especially since you could create a simple script that uses CASPOL.EXE to configure these settings and then distribute this script to everyone on your team.
Deployment Models for Users
You now know how to move code around your computer and share it with other developers, but what about your users? How are they going to locate your application? There are three ways to deploy a solution created with Visual Studio Tools for Office:
- Assembly on the network, document on the local computer
- Assembly and document on the local computer
- Assembly on the network, document on the network
All three options require changes to your user's local runtime security policy. In addition, if the host document is loaded directly from a network share, it must be explicitly trusted as well. Finally, don't forget that deploying any solution created using Visual Studio Tools for Office requires that the following be installed on every computer:
- Microsoft .NET Framework 1.1
- Microsoft Office Word 2003 and/or Excel 2003
Each deployment option has its own pros and cons. The choices for your solution and runtime security policy adjustments can range from distributing your application on floppy disk and installing it by hand on each individual client computer, to distributing batch files that invoke CASPOL.EXE, to full-blown distribution setups using standard *.msi files and Active Directory® Group Policy objects. That said, your network administrators have to decide what works best for them.
User Deployment Options
Deploying the assembly on a shared location (accessed either using file or HTTP protocols) is a popular option. Once users have their security policies set up correctly, they can run Excel 2003 or Word 2003 documents and receive the latest version of the assembly without being aware that they're retrieving a new copy. In addition, even mobile users can use this feature as long as they've run the solution at least once because the Visual Studio Tools for Office loader (via the CLR) copies the assembly to the offline assembly cache after it has been downloaded to the Internet Explorer cache. To work offline, the user must open Internet Explorer and indicate that he wants to work offline. If the user clears the Internet Explorer download cache, the solution will fail to load.
Modifying the user's runtime security policy so that the downloaded code runs is a little more complicated. If you're using an intranet location (regardless of the protocol you're using to locate the assembly), follow the steps that we outlined earlier and place the child code group under the LocalIntranet_Zone node instead of under the My_Computer_Zone node.
Naturally, you should use a production strong name instead of a test version for your users, as we did earlier in this article. In addition to giving the assembly a strong name, you might want to consider using an X.509 certificate (the kind you might have used for Authenticode signing) and a Publisher signature as the evidence. Although using strong names or X.509 certificates is more secure because they both rely upon cryptographic signatures that are significantly harder to spoof than simple URL or Site evidence, Publisher evidence provides the extra benefit of giving information about who created the code. In contrast, strong names are completely anonymous. It is only through out-of-band techniques that you would be able to verify that a strong name comes from a particular publisher.
If the assembly needs to be accessed over HTTP via a dotted location (IP address or DNS entry like msdn.microsoft.com), you need to either augment the user's Internet_Zone code group or create a new code group. Although you could allow code from any Internet location with your strong name (or X.509 signature) to run, it's generally better to create a special code group with a membership condition that matches just your site. This reduces the possible attack surface for those with malicious intent.
For our test computer, we created a code group at the same level as the My_Computer_Zone. We named it msdn.microsoft.com, set its membership condition to use Site evidence, set the site name to msdn.microsoft.com, and set the permission set to Nothing. Do the same thing on your computer but replace msdn.microsoft.com with your company's site. In the same way you added a child code group to the My_Computer_Zone, you'll want to create a child group under the site code group created in the previous setup. Use either your Publisher signature or your strong name for the membership condition and assign FullTrust as the permission set. Figure 4 shows our test computer configured to support our production strong name from the My_Computer_Zone, the LocalIntranet_Zone, and msdn.microsoft.com (which uses Site evidence). In this example, we chose to update the Machine policy level. You can choose any of the three levels, whichever best suits your needs.
Figure 4 Supporting Multiple Locations
If you choose to deploy both the assembly and document to the user's computer, you need to update their local policy. The benefit of giving the user both the documents and assembly is that there are no online versus offline issues for the user to handle. That said, if you need to deploy an updated version of your assembly, you must distribute the updated assembly to each user's computer.
The last option has the user running both the host document and assembly from a network location. In order to run the assembly, you'll need to configure the user's runtime policy, as discussed previously. There's an additional step: running the Office 2003 document remotely requires that you install a special DLL (msosec.dll) in the user's Global Assembly Cache. Once this is done, create two custom code groups. The parent is the location you want to trust using URL or Site evidence. The second code group uses a custom membership condition defined in the file msosec.xml. Step-by-step details are provided in the Visual Studio Tools for Office documentation, and are beyond the scope of this article. Once you complete these steps, you can run both the document and assembly from a file share location or Microsoft SharePoint™ Services server, for example.
Security is Vital
Is all this effort worth it? You bet it is. Remember how much pain and suffering the Melissa virus caused the Office and Windows communities at large? Microsoft Office 2003 is a great platform on which to build solutions, but everyone's wary of the next Melissa. By creating an environment where only explicitly trusted code runs, we're all better off. Visual Studio Tools for Office coupled with Office 2003 and the .NET Framework 1.1 provides a solid, more secure platform to build compelling document-centric solutions using Excel 2003 and Word 2003.
For background information see:
The Security Infrastructure of the CLR Provides Evidence, Policy, Permissions, and Enforcement Services
Brian A. Randell is a senior consultant with MCW Technologies and an instructor for DevelopMentor. Brian speaks regularly at VSLive! and Tech—Ed. He is the coauthor of Effective Visual Basic (Addison-Wesley, 2001).
is a senior consultant with MCW Technologies. He is coauthor of ASP
.NET Developers Jumpstart
(Addison-Wesley, 2002), Access Developer's Handbook
(Sybex, 2001), and VBA Developer's Handbook
, 2nd Edition
(Sybex, 2001). Reach him at firstname.lastname@example.org