Friday, April 25, 2008

Custom security and SqlDependency don't mix

Custom security objects may seem like a good idea in a web application. The .NET security model is extensible making it easy to store more than just the basic information in the security principal (IPrincipal).

Having said that, there is also a potential problem with this when the security principal crosses appdomains. Crossing an appdomain boundary requires serialization on both sides and that's where things can go wrong.

If you run a basic web application odds are you don't register your assemblies in the GAC; that's what xcopy deployment is all about, right? Well that also means deserialization is impossible because the receiving app domain is likely not to be aware of your web application's /bin folder.

So, make sure this doesn't happen, you might think. Well, in some cases you don't have a choice. A prominent example is SqlDependency.Start. Internally the SqlDependency creates a new app domain to handle the SqlServer notifications and it will fail if you're using custom implementations of IPrincipal and/or IIdentity that are serializable. Note that if you don't make these classes serializable SqlDependency will work.

I came across this issue after switching to out-of-process session state on one of my projects. Out-of-process session state requires serialization. Since objects representing users were stored in session state, I implemented serialization support on them. All of the sudden, SqlDependency.Start starts throwing exceptions like "Assembly not found". Thankfully, Microsoft has blessed us with the sources to the .NET framework which made debugging this issue a lot less painful.


h0ckey09 said...

Thanks for this post, I've been stuck on this issue for some time now. I have custom implementations of the IIdentity and IPrincipal as well. They are marked as serializable, and so when I call SqlDependancy.Start() I get a serialization exception, stating that one of my assemblies "xxxxx.dll" was not found.

I know this post was some time ago, but do you know how to get the newly created appDomain to load the assemblies the primary appDomain has loaded? Or any other workaround to this problem?

Thanks for this post and any help you can provide.

Marnix said...

The only workaround I could find in the end was to not user custom implementations of IIdentity. I reverted back to using the framework implementations.

h0ckey09 said...

I was able to install the assembly in the GAC, but this was not ideal. Also, ran into other configuration errors when I did that since it didn't use the app.config. I added a method to my IIdentity class, RevertToWindowsIdentity() and ReinstateCustomIdentity(). The method switched the tread identity temporarily back to the underlying windows identity, and then the reverse for method 2. I called them between the SqlDependancy.Start() and that worked successfully.

Thanks Again.