Dr. Strongname, or: How I Learned to Stop Worrying and Love the URL

Dr. Strongname, or: How I Learned to Stop Worrying and Love the URL

  • Comments 9

One of the problems with the Trustworthy Computing initiative is that many of our products have become harder to use as a result, either due to configuration changes or documentation changes. For example, Windows Server 2003 now ships with pretty much everything turned off by default, but customers that just want to "plug it in and go" get frustrated because now they have to configure everything that used to just be turned on automatically.

For VSTO, the problem is more to do with the way we document / blog about / evangelise the product to customers. Whereas in the past the documentation might have told customers the easiest way to accomplish something and left the more secure method to a side note or appendix, nowadays we focus on the more secure methods first and this can also be frustrating for customers who are willing to make the usability / security trade-off in favour of usability.

So, customers hear about VSTO, they hear about its strict security requirements, and they hear about this thing called "strong naming" and conclude that they need to strongname their code in order for it to work. It doesn't help that every time someone asks a question about VSTO security, we usually say "Well, you could just trust the folder, but that's not really recommended - you should use a strongname to be more secure!" But that's pretty much the responsibility we have with Trustworthy Computing; we need to tell people the "better" way of doing things rather than the "easier" way.

Anyway, the problem with strongnames is that they weren't really designed to be used as security principals -- they were designed to uniquely identify assemblies and to detect tampering or spoofing attacks against assemblies. In order to ensure that two assemblies with the same name get different identities, strongnames use the same kind of public-key digital signature technology that Authenticode signing does. This means that, cryptographically speaking, the kinds of guarantees that the system can make about an assembly are the same for both strongnames and Authenticode signatures. Couple this with the fact that anyone can generate a strongname keypair for free with sn -k mykey.snk (versus setting up a certificate server or dropping cold hard cash to Verisign or another 3rd party CA) you can see why strongnames are a popular way to trust assemblies.

But strongnames are first and foremost are about guaranteeing the identity of an assembly, and one of the properties of an assembly's identity is its version number. When one assembly Budget.dll is dependent on a strongnamed assembly Helper.dll, the information baked into Budget's manifest includes the version number of Helper as part of the reference information. If, at runtime, the CLR cannot find the exact version of Helper that Budget was originally compiled against, it will fail to load the assembly and probably cause the application to die horribly as a result. (Publishers can supply version-redirection information with their assemblies to modify this behaviour, but that's another complication I don't want to get into right now).

So let's say that you build an Excel-based VSTO solution with Helper.dll version 1.0.0.0 and Budget.dll version 1.0.0.0, you put them both on a share, and people start using them. Someone reports a bug in the application, and you track it down to a problem in Helper.dll, which you re-build as Helper.dll version 1.1.0.0 and upload it to the share, over-writing the old version.

But now you get calls from people saying that the solution is broken, so you run the app in the debugger and determine that it is because Budget.dll is looking for version 1.0.0.0 of Helper.dll, but only version 1.1.0.0 is available. Since you probably didn't know about <bindingRedirect> and/or you don't have time to investigate further, you take the easy way out -- just change the version of Budget.dll back to 1.0.0.0, re-build it, and ship out the "new" version of the assembly to the share again.

Ah-ha, but now something else goes wrong. You get calls from a different set of users who claim that you haven't fixed the bug at all -- it is still happening on their machines. You double-check to make sure that the "right" version of Budget.dll version 1.0.0.0 is on the share, and sure enough it is. You fire up the VSTO solution on your desktop and verify that, sure enough, the fixed version of the assembly is loading. Just to be sure, you make some additional changes to the code (like, say, putting MessageBox.Show("This is the fixed version!") in the constructor), re-build it, copy it to the share, and run the solution again.

Hmmm, that's weird -- the message box never popped up. You do a bit more poking around and add the following line to the assembly:

  MessageBox.Show(this.GetType().Assembly.Location);

Now when you run the solution, you see that the assembly is running from some seemingly random location in your profile folder -- not from the network share where you would expect it to be loading from:

c:\documents and settings\joe_user\local settings\application data\assembly\dl2\a7dn830f.ad2\aofhwldc.vc0\72d679a8\8368c70e_93af78bc\helper.dll

What's happening? Well, when you open the project, the CLR loads Budget.dll and sooner or later it tries to use a type defined in Helper.dll. Fusion is asked to load the assembly, and since it has a strong name with the version 1.0.0.0, Fusion looks in the local download cache to see if it already has that assembly -- why download it again over a slow network link when you can load it right off the local disk (and Windows may already have it mapped into memory, too)? Of course Fusion sees that it does already have a local copy of version 1.0.0.0 of Helper.dll, so it loads that without going out over the network to download the other copy -- after all, you strongnamed the assembly so it's guaranteed to be the right one. If it had been changed, the digital signature wouldn't match.

But of course it's the "wrong" copy of version 1.0.0.0, because you made changes to it but didn't update the version number. You're lying to Fusion about the version of an assembly, and then you're wondering why the system isn't working as expected. (As an aside, the first set of users who complained about getting assembly load failures due to the wrong version number would have been the ones who had never previously downloaded the buggy version of Helper.dll, so Fusion didn't have a copy in the cache).

So now you're really in a bind:

·You have to use strongnames, because you need to be secure

·But if you use strongnames, you have to keep updating the version numbers to play by Fusion's rules

·And if you update the version numbers, your applications break

·But the only way to bypass Fusion's cache lookup is not to use strongnames

· And you have to use strongnames!

·Argh! What's a poor developer to do?!?

Well, take a step back; as I noted in a recent post, security is all about managing risk. If your efforts to be secure cause more problems than they solve, it may be time to re-think your strategy.

We recommend that users rely on some form of cryptographic evidence such as a strongname or Authenticode signature because it provides a high degree of assurance that the assembly you are running really is the one you think you are running -- even if a malicious user can gain write-access to the location of your assembly, they can't modify its contents or replace it with an entirely different assembly without breaking the signature and thereby causing the CLR to reject any requests to load the assembly. Only if the malicious user has access to your private key (ooops!) and they have write access to the assembly's location can they cause damage in this scenario.

But if you have good controls over access to the assemblies' location (ie, you ensure that only trusted parties can upload or modify files on the server) then you may be comfortable with just using URL-based evidence for your VSTO solutions. Besides, in a typical scenario there are likely to be other things on that server that a bad guy could take advantage of if they gained write-access to it, so you're really no less secure than you were before by relying only on URLs.

So, if you're running a controlled environment inside the firewall, maybe you can drop the strongnames (and the versioning issues that come with them) and just rely on publishing your assemblies to a trusted server location. For information on how to set up policy based on URLs, you can consult the VSTO documentation.

  • > you put them both on a share, and people start using them.
    >
    I was bitten by a related issue recently. The worrying thing is it doesn't even have to be a share. If your assembly gets loaded into an app domain with shadow copying enabled it will get copied to the download cache. In future this cached version will be used even if shadow copying is disabled!

    This means you can't count on being able to update an assembly (without changing the version number) even in a pure xcopy scenario. This is so counter intuative and broken! What is the alternative? What if you're using 3rd party strong named assemblies? Must they always change the version number, even for bug fixes?
  • Yes, it means that if you are using shadowcopy (which VSTO does to enable you to replace assemblies that are "in use" the same way that ASP .NET does).

    They should ship binding redirect files with their components, or you can write your own -- follow that link in the main blog text.
  • I am very aware of this problem. But there is not much we can do. Some people are not happy about this. Of course, if we always go to the source and do the download, many other people will not be happy. We cannot satisfy all.

    I made a change in Whidbey Beta2 to not look at download cache if the assembly is in local disk. Jamie, that at least solves your problem.
  • If you're "running a controlled environment inside the firewall", it should be reasonably simple and cheap to set up a CA and issue your own authenticode certificate, so there's little reason to dispense with all safeguards merely to avoid versioning an assembly.
  • Nicole,

    From what I've heard, the technology for setting up a CA is easy; it's the planning and the politics that are hard ;-)

    You don't just slap up a CA and go home for the weekend... there are lots of things to consider:

    http://search.microsoft.com/search/results.aspx?st=b&na=88&View=en-us&qu=PKI+planning
  • > I made a change in Whidbey Beta2 to not look at
    > download cache if the assembly is in local disk.
    >
    Junfeng, That's great, thank you very much. The problem at the moment is people don't know about the issue. I now think about it every time I strong name an assembly.
  • I like the sidebar.

    Element
    Element
    Element
    Element
    ...

    Someone forgot to escape html-like tags when designing MSDN library. ^_~
  • Obviously one doesn't just "slap up" a CA, but I would argue that it's still considerably simpler than the Microsoft documentation on the topic might leave a poor developer to believe. Many of the concerns raised in the docs are targeted at larger organizations, which will either already have a CA in place or have no problem dropping a couple of hundred dollars on a commercial authenticode certificate.

    For very small organizations, the planning and politics can often be near non-issues. The biggest obstacle to implementing a CA in such organizations is very often the perceived complexity of setting of the CA itself, and this perception very often comes from an attempt to process the documents on your referenced list.

    Setting up a minimal CA for dev purposes (issuing of SSL and authenticode certificates only) is a fairly trivial exercise. I suspect that you would see a lot less resistance to it if there were some reasonable documentation targeted at this fairly common scenario.
  • doesn't windows server ship with a CA built in? then the only issue is deploying the root cert. and handling the meainingless security dialogs. ("Hey, something reallly possibly dangerous is gonna happen with your cert store. OK/Cancel.") (i swear the old ones provided enough info to make a decision, even if the average user didn't understand it.)
Page 1 of 1 (9 items)