Common Security Pitfalls for Web Part Developers
I've wanted to write this post for a long time, the trigger to actually write it was my interview by Chris Kunicki for his Office Zealot podcast last week. During this podcast (which was really fun to do, thanks for having me Chris!) we discussed briefly some security problems web part developers could face. Podcasts are great but they are not suited to communicate code, so here's the accompanying post! When you're developing web parts, security could be one of the biggest problems to deal with. First of all you may face some problems to get your web part deployed and secondly you may have some Code Access Security problems. Let's take a look at both of them!
When you develop web parts using the traditional way (using the Visual Studio.NET Templates), you have to create your web part class and a DWP file before you can deploy your web part. Additionally you must alter the Web.Config of your SharePoint site, and add your web part in the SafeControls section. Once everything is in place, and you want to drag and drop your brand new web part on a page, you may get the message "A Web Part or Web Form Control on this Web Part Page cannot be displayed or imported because it is not registered on this site as safe". If you get this message, either something is wrong in your DWP or something is wrong in your Web.Config. Common mistakes are:
- Wrong AssemblyName in DWP: if you have a strong named assembly (using a SNK file) your assembly element should contain the complete name (including the version, culture and public key token. An easy way to retrieve that name is the using the Reflector tool.
- Wrong Assembly in Web.Config: same remark, the Assembly attribute should contain the complete name of your assembly.
- Wrong TypeName DWP: the type name of your class is prefixed with the namespace. By default your namespace is the same as your Visual Studio project name.
Example of a DWP file:
<?xml version="1.0" encoding="utf-8"?> <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2" > <Title>SmartPart 1.0.0.0</Title> <Description>The SmartPart User Control Webpart</Description> <Assembly>SmartPart, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=dd064a5b12b5277a</Assembly> <TypeName>SmartPart.UserControlWebpart</TypeName> </WebPart>
Example of SafeControl node:
<SafeControl
Assembly="SmartPart, Version=1.0.0.0, Culture=neutral, PublicKeyToken=dd064a5b12b5277a"
Namespace="SmartPart" TypeName="*" Safe="True" />
Using the SmartPart to create web parts solves these issues. You don't need to create a DWP file and you don't need to change your Web.Config! Once you have deployed your web part, you may have some problems regarding Code Access Security. Out-of-the-box SharePoint is running with a trust level that contains very limited privileges. For example: you can not access the object model or a web service; this will give you a security exception. If you take a look at the Web.Config file, you will see that SharePoint is running using the WSS_Minimal trust level (<trust level="WSS_Minimal" originUrl="" />). Where is this WSS_Minimal defined? Well if you look in the securityPolicy section of the Web.Config you will find:
<securityPolicy> <trustLevel name="WSS_Medium"
policyFile="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\config\wss_mediumtrust.config" /> <trustLevel name="WSS_Minimal"
policyFile="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\config\wss_minimaltrust.config" /> </securityPolicy>
So WSS_Minimal is pointing to a trust level which is defined in the wss_minimaltrust.config on your hard disk. As you can see, another default trust level is WSS_Medium which will add permissions to access the object model. If you write a web part that uses the SharePoint object model, make sure you set the trust level to WSS_Medium. What about web parts that contain really dangerous stuff like accessing a web service or, even worse, writing a file on the hard disk? Well you need to make sure that you web part is running with elevated privileges. So far the theory, how can we do this? There are several possibilities:
- Set the trust level in the Web.Config to full. This will solve all your CAS problems because everything will be running with full trust. Of course this is not a good solution, so use it only on your dev machine for testing reasons!
- Deploy your web part assembly to the GAC instead of to the BIN folder. Assemblies deployed to the GAC will run with full trust, so it solves all CAS issues for the assembly in the GAC. Is this a good solution? In my opinion this is abusing the GAC to gain full trust permissions. I know some people would argue differently, but my advice: do not abuse the GAC for this. Especially during your development cycle, assemblies deployed to the GAC are cached so you will need an IISReset when you deploy a new version.
- Create a custom policy file that will grant full trust to your own assembly. This is my favorite solution; it's the most difficult to implement, but it's very secure and you only have to do the work once.
In the custom policy file, you can grant for example full trust permissions only to your code. But what is your code, how do you identify your code? Well (once again in my opinion) the nicest way is to grant full trust to code that is signed with a specific public key. It's very secure and reusable, as long as you keep using the same public/private key pair (SNK) to sign your code, all your code will be running with full trust. If you want to use this technique, follow these steps:
- Create your web part, and give your code a strong name by using a public/private key pair (SNK).
- Create a customized policy file. The easiest thing to do is to start from the default policy files, for example the wss_mediumtrust.config file.
- Copy that file and name the new file wss_customtrust.config for example. In this policy file you will need to add the following section, right after the FirstMatchCodeGroup:
<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust"> <IMembershipCondition version="1" class="StrongNameMembershipCondition"
PublicKeyBlob="your public key blob"> </IMembershipCondition> </CodeGroup>
- By adding this section to the policy file you will assign full trust permissions to all the code that is signed with that specific public key blob.
- How do you retrieve the public key blob? The easiest way to do this is to use following little trick:
- Start the Microsoft.NET Framework 1.1 Configuration (from the Administrative Tools)
- Open Runtime Security Policy/Enterprise/Code Groups/All Code (actually what you open doesn't matter because we're not going to make any changes in this tool).
- Right click on the All Code node and choose "New..."
- Choose "Dummy" as the name (don't worry we are not going to save this!)
- As the condition type choose Strong Name.
- Click the Import button and point to your web part dll.
- The public key blob will appear in the text box so you can easily copy and paste it in the IMembershipCondition node.
- Cancel everything in the Microsoft.NET Framework 1.1 Configuration tool. (Do not save any changes!)
- Once you have saved the wss_customtrust.config in the same folder as the default policy files, you need to alter the Web.Config file so your new policy file will be used. So change the trust node to
<trust level="WSS_Custom" originUrl="" />
In the securityPolicy node, add following node:
<trustLevel name="WSS_Custom"
policyFile="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\config\wss_customtrust.config" />
You're done. When you test your hard work, you may end up with the "Assembly <assemblyName> security permission grant set is incompatible" exception. Don't worry, you'll need an IISReset. To give you all a head start I've uploaded my own prepped wss_customtrust.config. It's a modified version of the wss_mediumtrust.config that already contains the UnionCodeGroup to give code singed with a specific public key blob full trust. The only thing you need to replace is CHANGE_THIS, put in your own public key blob over here. This may look like an awful lot of work, my advice do it right the first time and you won't have any CAS issues anymore.
Although using the SmartPart to create web parts has some advantages, it doesn’t solve your CAS problems. Make sure you’ve assigned a strong name to your Web Application project that contains your user control, and use that strong name in a custom policy file as described above.
If you want to learn more about CAS, I would recommend visiting Maxim Karpov’s blog. He has written a couple of must-read articles and he has helped me with my own CAS problems some times. :-)