Why must I trust the documents as well as code?

Spurred on by the knowledge that at
least 5 people read my blog
and the fact that it's 2:40 am the morning before I'm supposed to
give a talk on Office security, I thought I'd post some more stuff.

For those of you who didn't go to TechEd and learn all
about VSTO, we have
this new product called VSTO (technically, it is "Microsoft Visual Studio
Tools for the Microsoft Office System", but that's a mouthful) and it lets
you build managed code behind Word and Excel docs. Much like the WordBlogX
sample I have on GotDotNet
(downloaded 111 times so far...).

Anyway, security is of course a huge concern with Office
development, so we had to get it right (for some interpretation of the word
"right"). The CLR has a very
rich security system
, but up until now most developers haven't really had
to deal with it.

Why? Well, mostly because they are...

  • Using
    ASP .NET, where you have FullTrust by default; or
  • Building
    local EXEs, where you have FullTrust by default; or
  • Building
    EXEs or DLLs on an Intranet, where they only have partial trust but can
    work within those limits

Now we come along with our little product and say 'You MUST have FullTrust to use this code.
Oh, and by the way, you MUST have a
better reason than "I've been copied to the local machine!" in order
to do that.' The main motivation behind this is that it's very easy to socially
engineer people into downloading content to their local machine. There have
also been bugs in the past whereby attackers could place files in well-known
locations on your hard drive, ready for future exploitation, and we wanted to
avoid these scenarios.

So anyway, back to the point of this post... most people
understand that you need to trust CODE
before it is allowed to execute, because we don't want arbitrary code from
ne'er-do-wellers running amuck on our machines, now do we? Right. But why do we
have to trust VSTO documents as well in order to get stuff to work?

Look at it this way. You probably have some sharp knives in
your kitchen drawer, and you probably trust at least some of them to cut
through various food substances without hurting you or damaging the
aforementioned foodstuff too much. (You may also have some blunt old knives
that you wouldn't even trust to cut through hot butter -- time for some spring
cleaning, donchathink?). Even though these knives are very sharp and could
potentially do lots of harm, when used correctly by an educated, non-malicious
person, they are quite useful.

OK, so you trust the knives. But do you trust your (perhaps
hypothetical) young children with the knives? No, because they are not aware of
the dangers associated with knives and may accidentally hurt themselves or
other people. Respect the knife!

Similarly, if a big bad guy in a trench coat breaks down
your front door and demands all your money, would you trust him with your
knife? No, because he's probably going to do something quite nasty with it. (I
once wrote an internal e-mail about trust vs safety that ended in a comment
something like "No matter how safe your scissors are, it's never a good
idea to hand them to a criminal as he's breaking through your front door."
I thought it was quite funny at the time.)

See the link here? Just because the knife is trustworthy when
handled by a trustworthy person, doesn't mean it will be trustworthy when
handed by an untrustworthy person. And just because code is trustworthy when
invoked by a trustworthy document, doesn't mean it will be trustworthy when
invoked by an untrustworthy document.

Another quick analogy: it's OK for me to use the del command to
delete files, but it's not OK for random web pages to use the del
command to delete files. The same idea applies to the Restricted Sites zone in
Internet Explorer (and subsequently any HTML e-mail you receive) -- just
because an ActiveX control (such as MSXML or Flash) is "safe" when
used by a trustworthy web page, doesn't necessarily mean it is "safe"
when used by an untrustworthy web page. (Obviously both MSXML and Flash try to
be safe at all times, but both have had security problems in the past that let
the bad guys do bad things). So it's never a good idea to enable Flash in
e-mail (or any other "active" content for that matter), because the
bad guys could send you a mal-formed Flash file that takes over your machine.

So in an attempt to try and minimise this kind of thing (we
call it a "re-purposing attack"), VSTO requires that a document must
be trusted before it can host code, and furthermore that the code it is trying
to host must be trusted. Now unlike code, which is NOT trusted merely be being
copied to the local machine, documents ARE trusted if they're copied to the
local machine because, basically, there's no other good way to secure
documents. You can't use a signature because documents change constantly, and
although you could use a specific directory (eg "My Documents")
people tend to place documents in all manner of random places, so it would be
too hard to manage.

What about documents on the network, you ask? Well, here's
another problem.

Say you have a SharePoint site at https://MyDocs/
and you want to let people upload VSTO documents to the site. You've already
followed best practices for the actual code, so it is signed with the corporate
key and stored on a secure, read-only server somewhere else (eg https://MyAssemblies/). Great, no-one can tamper
with the assembly unless they compromise the server AND steal your private key,
in which case it's time for you to take an emergency extended vacation anyways.

But what about the documents? You have to grant them
FullTrust, otherwise they won't be able to load code. But you have some
problems:

  • You
    don't want to grant FullTrust to the entire site, because anyone can
    upload junk to the server (including EXEs or DLLs) and you don't want that
    junk to get permissions
  • You
    can't use hashes or signatures or anything else, because docs don't lend
    themselves to those kinds of evidence
  • You
    can't really trust each document individually (by name) because that's a
    management nightmare

Pop Quiz, hotshot: You've got a document on a share and you
want to give it FullTrust. Whadda you do?

Answer: Shoot
it in the leg
. No, wait...

What you do is use the new OfficeDocumentMembershipCondition that ships with VSTO. With this
you can set up a rule in policy that says "Grant Office Documents
FullTrust" (obviously you would scope this to your MyDocs server or your
Local Intranet to avoid leaking permissions out to the world...). When Office
loads a VSTO document, it creates the AppDomain passing in the location of the
document and the special OfficeDocument
evidence. This additional piece of evidence can be used to grant permissions to
documents (using the ODMC mentioned
above) without granting them to EXEs or other code, since no other code will
present the  OfficeDocument evidence.

:> cd
"%programfiles%Microsoft OfficeOffice11Addins"

:> gacutil -i msosec.dll

:> caspol -m -ag
LocalIntranet_Zone -site MyDocs Nothing -n MyDocs_Site -d "Container for
MyDocs HTTP server"

:> caspol -m -ag
MyDocs_Site -custom MSOSEC.XML FullTrust -n OfficeDocuments -d "Allows
Office Documents to host code"

See, it's easy really. You can even do it through the GUI (mscorcfg.msc) if you want.

Bonus points for anyone who can tell me why we don't install
ODMC in the GAC by default...

[And now it's 3:40
am...]