The question comes up all the time across hundreds of .net forums. Here it goes:
"I just created a vb.net application that works perfectly fine on my local machine, however I need to run this one application on a server so other users can use it. When I try to run it on the server I get a TON of errors."
The error generally looks like this (warning: It's pretty long and ugly):
So the question is why does this happen now in .net, how can I get around it, and how do I stop it from happening in the future. Before I get right down to the answers I would like to explain this (so you fully understand why this happens).
VB6 Gone Are The Days
Gone are the days where you could write a VB6 application, compile it, throw it on a UNC shared path and apply some security rights to your end users. That was too simple...but in reality it was a pretty poor method of doing things. You would have to constantly be modifying the rights on network folders, your admins hated you with a passion.
CLR and CAS
With the .net framework programs that run on the CLR use CAS (code access security). What this means is you no longer assign rights to users / groups on a network. Instead you assign trust to actual code that the programmer has developed. Basically your applications assembly file is compared with the security policy of the machine. When you run your application on your local machine and it works just brilliantly fine it's due to the fact that you are running your code in the MyComputer zone. By default the MyComputer zone has FullTrust (unrestricted permissions) to do virtually anything. This is why the application works on your local pc just fine...now why is it not working on the server.
It Just Wont Work On That Server
Your application bails on the server because the server is more restrictive given its zone is the LocalIntranet zone which does not have full trust to run an executable by just anyone from any machine. In addition, it is a lot more secure (and that's the whole idea behind microsoft security from now on, make it more secure). It also makes a lot more sense since now not anyone can just throw executables in a server shared and run them.
Lets Discuss a Complete Example
Pictures are worth 1000s of words..ya...so it's best to stop yapping and to start showing. Let's go through a full blown example of an application that works fine on my local pc and bails on the server. First and foremost it's worthwhile to note that on most occassions applications bail on things that access other network structures, SQL data retrieval (working with the database), etc. The reason why I have placed a bold item on the second item is because that is what we will work on as well as in most cases this is what most people run into problems tapping into a database.
So first and foremost the application. We are going to write a useless application that simply pulls EmployeeID numbers from the Northwind database (The sample database given to us by MS on SQL Server). The application has no use nor will we be writing pure beautiful code that takes advantage of stored procedures, triggers, yada yada yada. The point of this post is not how to write clean code that taps into a database, it is simply to demonstrate how to avoid the security policy error in .net.
So lets start by creating a VB winform application.
I'm using VS 2005 so you may have to work around if you are using 2003..the code and the methodology is the same in both.
Select Visual Basic and highlight Windows
Select Windows Application
For the name you can call it AvoidSecurityException and store it anywhere (I usually use C:\Temp for temporary projects, and you can call it whatever you want.
Now we are going to work on our project. Drag and drop a Button onto your form, it doesnt matter where.
Double Click the button so that it takes you to the code behind page. Since we will be dealing with databases you need to import the System.Data.SQLClient namespace. So above the public class Form1 add the text Imports System.Data.SQLClient.
Now we have the SQLClient namespace we can start writing our code in the Button1_Click event.
We are pretty much done, told you it was a worthless application, just to show you what happens when placing this app on a server as opposed to your local machine. Now go ahead and build your application:
From the menu at the top select Build->Build AvoidSecurityException (the name might be different here).
Once you build this application you can run it on your local machine. Either run it within Visual Studio, or double click the executable found in your projects bin/debug directory. You will notice a simple loop that message boxes the employee id numbers, hopefully there isn't alot, if there is you will need to end the program (ctrl alt delete).
Great your program works, now you need to deploy it to a server so that all your other users on your network can use this program. So you copy and paste your .exe file (the one that was in your bin/debug directory) to a network server share folder \\servername\folder. Now you double click the exe from that shared network folder and you get that same message about an unhandled exception all the way down to System.Data.SqlClient.SqlClientPermission.
How To Avoid This
There's a couple of ways to avoid this. You can do it the right way which takes a bit longer and seems to be a bit more complex, or you can do it the other way which is much quicker but is poor practice and a security vulnerability.
The 2 ways are:
- Create a strong name key and use it in your applications AssemblyInfo.vb file
- Modify the security policy to fully trust the LocalIntranet zone
The 2nd method (bad way of doing things)
(From the .net security blog: http://blogs.msdn.com/shawnfa/default.aspx)
The easiest way to modify your security policy is by using the Microsoft .NET Framework Configuration utility from the control panel. You can also run this tool from the command line by running mscorcfg.msc.
- Expand the Runtime Security Policy folder
- Expand the Machine policy level
- Expand the Code Groups folder
To modify the polcy to trust a specific strong name:
- Right click on All_Code, and select New
- Create a new code group for your strong name, and hit next
- Select a strong name membership condition from the drop down box
- Hit the import button, and select your assembly. The configuration tool will import your public key. If you want to trust everything you sign with this key, leave the name and version boxes unchecked
- Select the FullTrust permission set
To modify the policy to allow full trust for all Intranet assemblies:
- Expand the All_Code code group
- Right click the LocalIntranet_Zone code group, and select properties
- Switch to the Permission Set tab, and select FullTrust
Reference: .net security blog
The Right Way of doing it
The correct and more preferred method is to create a strong name using the sn.exe tool and using that key in your application's AssemblyInfo file to get around these horrendous errors. So point to your visual studio 2005 program group from the start menu. Select Visual Studio Tools then select Visual Studio Command Line
The command line will open. Type in:
sn -k mykey.snk
You can change mykey to any value you want such as myappskey.snk the main thing you want to make sure is you run it with the -k option.
sn -k myappskey.snk
In my apps example I will stick with mykey.snk. Result should look much like this
Notice how the key has generated and written as mykey.snk within the active directory. Simply take and move this key to where you would like to store it. Make sure you place it somewhere visible to the application, in addition do not store it in a local protected folder while the application is sitting on some shared server. It needs to be able to see it (does not mean it has to physically see it within the same directory, just means it should be able to call it or have visibility to it). Let us say you stored the key on some shared network drive. Fine and dandy, now we need to modify our applications AssemblyInfo file to accept this key.
So go back to your project and look in the Solution Explorer for the AssemblyInfo.vb file. If you do not see it click the "Show All Files" icon (second small icon on the solution explorer). Then drill down into "My Project" and you will see it:
Double click this file, and you will be presented with something like so:
You will need to add the AssemblyKeyFile attribute to this so that your assembly is aware of your key. So you can add it anywhere in this file. I have added it under the AssemblyTrademark:
Notice it as:
You will need to modify the path to point to where you have stored your .snk file. Now rebuild your project.
Copy over your newly generated exe file to the shared network path so that all the other users can now run your application. Run your application from that shared folder on that server and Voila you no longer get the security exception error. The application message boxes the records from the database.
If You Still Get The Error
Strange, you shouldn't. But if you did make sure your key (.snk) file is visible on some shared network folder. Make sure your AssemblyInfo.vb file has an AssemblyVersion attribute (the line right before the last line in the image posted of the AssemblyInfo file). Make certain you got the latest .exe compiled and that you've copied that file over to the server.
Finally last thing you can do is to select Build->Publish Project from VS 2005 and tell it to publish the .exe file to a shared (UNC) server. Go through the wizard and get it to copy it automatically. Sometimes you will need to install the latest .net framework on the server that hosts the exe file.