Even the defense department security protocols are breached on occassion. So, to
think that the security policies we've put in place are 100% fool proof is a
bit naive. Securing applications is all about determining risk if a breach occurs,
lowering that level of risk, and reducing the number of people who are technically
knowledgeable enough to pull it off (and not all attackers have the same skillset
or background).
The end goal is to make it take more effort to breach your security protocols than
it is worth. Of course, this is easier said than done and often requires numerous
small steps to achieve. Personally, I like to apply both known best practices,
obfuscation, encryption, and a healthy amount of purposeful confusion.
Every application and its data is different which makes writing an article that gives
you a complete set of best practices impractical. What I'll do instead is to
give you some things to think about and some suggestions as to how you might
address them. For now, I'll focus strictly on Silverlight applications that reference
your WCF service. A typical Silverlight / WCF architecture looks a little like
this:
Silverlight -> SSL -> WCF Service -> Business Logic -> DataBase
Silverlight <- SSL <- WCF Service <- Business Logic <- DataBase
The areas in red represent potential security holes and the areas in green are your
last line of defense. Not only is Silverlight a red area but so is the SSL secured
network traffic between itself and the WCF service.
WCF Suggestions
1. ClientAccessPolicy.xml
This file helps you control which domains have access to call your WCF service. Here
is a very basic example of how you can restrict access to only those applications
running under your desired domain. This permits someone running your Silverlight
application from both a secure and a non-secure url.
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="https://www.yourwebsite.com"/>
<domain uri="http://www.yourwebsite.com"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
2. Secured Socket Layer Certificates (SSL)
Use secure socket layer certificates to protect "most" sets of prying eyes
from monitoring unencrypted network traffic. This requires an increased level
of expertise, hardware, and software to monitor your data transmissions. Assume
that someone knows how to use a networking tool like Fiddler to monitor your
Silverlight application's network traffic and thus discover your WCF service
url. Also assume they know how to use Fiddler's capability to decrypt SSL encrypted
traffic.
Yes, every piece of data you transmit back and forth from your Silverlight application
to your WCF service can be decrypted by tools like Fiddler. This refers specifically
to data you did not encrypt inside your application(s) and only to data encrypted
automatically via the SSL certificate. They would just fire up Fiddler and configure
it to decrypt SSL traffic. Then, launch your Silverlight application in their
browser. Fiddler would immediately decrypt all traffic to and from your Silverlight
application as it transmits/receives data from your WCF service. If you have
ever heard of "man in the middle" attacks, this is a good example of
a simplistic one.
All of that said, unless the end user is running an unsecured wireless configuration
on their laptop, the risk is low that your WCF service would expose something
to a hacker that is potentially harmful to another user.
However, you also need to protect your service from exposing information to your
user that you only want your Silverlight application to see. Application secrets
or proprietary company information that no one should ever see come to mind.
The rule of thumb I use when sending secrets over SSL is that if you don't absolutely need to send it, don't.
Here is just one example. If your application retrieves user accounts other than
the hacker's own account, they could decrypt SSL encrypted strings and expose
passwords belonging to other users. Even if the password itself is encrypted
in the database, having the encrypted value is one step closer to a breach. Since
the Silverlight application doesn't need to use the passwords of these other
users, one option is that we could just clear that property value in the WCF
service. You will need to evaluate the risk of sending each and every class property
over SSL and make the appropriate adjustments.
3. WCF Service Meta Data
If your service is only going to be used by your applications, you should not publicly
expose the meta data. It is the meta data that enables someone to most easily
determine what your service, operation, message, and data contracts look like.
Many client applications dynamically assign their web service urls at runtime.
However, your Silverlight application does not need to see the WCF service meta
data in order to create the endpoint binding to a different production web service
url at runtime.
Why not? When you created a service reference to your WCF service running on localhost,
those classes created by Visual Studio .NET that act as your service reference
can be pointed to a different service url at runtime. The classes are tied directly
to ServiceContract namespaces but not to the actual web service url. As long
as the service contracts that exist in your development environment are the same
as those you've deployed to production, it will work properly.
To hide the meta data, you should remove the highlighted line below from your web.config
file when you deploy this to production:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service behaviorConfiguration="Your Service Behavior Name" name="Your Service Name">
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
Also, you'll need to change the highlighted properties from true to false:
<behaviors>
<serviceBehaviors>
<behavior name="Your Service Behavior Name">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
4. Alter your service contract namespaces
[ServiceContract(Namespace = "https://www.google.com")]
public interface IServiceReference1
Various ServiceContract attributes will show up in the service references created by Visual Studio .NET.
Many of these references are not obfuscated or string encrypted (obfuscation
discussed in the Silverlight suggestions section) by obfuscation tools. They
are clearly visible using Reflector (also discussed below). So, make sure people
can see only what you want them to see. One last thing, if you change some of
these attributes, you will need to update the service reference in your Silverlight
application.
5. Customized Access Tokens
I like to require one or more custom access tokens be passed in to each WCF service
operation. The WCF operation would be responsible for validating the token prior
to operation execution.
6. Assume You Will Fail To Secure Your WCF Service
Someone with the right know-how and motivation may get by steps 1 through 4. You
should carefully review each WCF operation and what it does in order to determine
the risk if that operation is called inappropriately. Keep in mind, your operation
contracts can be called in any order and at anytime. You can't trust that a series
of steps has taken place prior to the execution of the contract. Also, critical
operations should not trust that the values of their input parameters have come
from their own client application or the user designated as having sent them.
Add extra parameters in these critical operation contracts requiring the user to
authenticate, validate permissions, retrieve existing records, and compare ownership/access
rights prior to allowing the query, data change, or data delete to occur. Not
every operation contract requires this level of complexity. Operation contracts
that return look up tables probably would not qualify. An operation contract
that enables user passwords to be changed probably would.
Silverlight Suggestions
1. Assembly Obfuscation and String Encryption
As I'm sure you know, tools like Reflector are out there that will let anyone disassemble
your .NET assemblies into readable and usable source code. Obfuscation and string
encryption of your assemblies makes this much, much more difficult.
There are numerous third party .NET obfuscation and encryption tools. The free versions
typically aren't secure enough for corporate applications. Most of these tools
are used on a single build machine. It is important to note that many obfuscation
tools do not come preconfigured to obfuscate Silverlight assemblies. You will
most likely need to include the paths to your version of Silverlight in their
user defined assembly path section.
After you have compiled your application into a .xap file (compressed file containing
your compiled Silverlight assemblies) via Visual Studio .NET, copy your .xap
file over to your build machine, obfuscate, string encrypt, and then zip up the
obfuscated assemblies into your deployable .xap file. You could use WinZip, 7Zip,
or just about any other compression tool to recreate your .xap file.
Most current obfuscation software will cause your Silverlight application to choke
at runtime if you attempt to obfuscate an assembly with XAML in it. They just
don't handle this well. So, keep all mission critical C# code that you don't
want people to see (which means every single line you can possibly obfuscate
:) in separate assemblies. The Model/View/ViewModel pattern takes care of this
for us if you put the Model and ViewModel in different assemblies from the View.
Follow the pattern and you'll end up with XAML code behind classes that you could
care less whether someone can view.
2. Runtime Reflection
You may be tempted to hard code secrets into your assemblies versus exposing even
an encrypted value in a config file. Should you do this, remember that these
secrets can be extracted at runtime using reflection. As an example, someone
could take your .xap file from their browser cache, decompress it, and then include
some of your assemblies in their own Silverlight application. Fire up the debugger
in Visual Studio .NET and start stepping through code looking at method return
values and property values.
Paste the following code in a .NET console application and you'll see what I'm referring
to. Notice how it
was even able to extract values of private class level variables. However, Reflection
cannot interrogate local variables declared inside methods and event handlers.
using System;
using System.Diagnostics;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var blog = new Blog();
var fieldInfos = blog.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo fieldInfo in fieldInfos)
{
Debug.WriteLine(fieldInfo.Name +" "+ fieldInfo.GetValue(blog));
}
}
}
class Blog
{
private string _test = "this is a reflection test.";
public string MyProperty { get; set; }
}
}
Another trick centers around hard coded strings that you may think are hidden by
using the obfuscation and string encryption process. You are partially correct.
Tools like Reflector won't be able to expose the string encrypted value but runtime
Reflection certainly could.
If you really need to store critical secret hard coded strings, ask yourself if the
Silverlight application needs to decrypt them or not. If not, encrypt the string
via some other tool (maybe a whipped up console app of yours) and paste the encrypted
string in your code instead and remove all references to the encryption key from
your application (and never transmit it to or from your Silverlight application).
Again, it is just one more level complexity for someone to have to work through.
3. Silverlight 3.0 Does Not Support Code Access Security
Without getting into too much detail, I often place very low level hidden environment
oriented checks in various assemblies preventing their misuse outside my applications.
Let your imagination run wild here.
4. Wrap Business Logic Classes Around WCF Service Clients
Try to set as much of your code that directly interacts with your WCF service reference
to private or internal. Only expose your wrapper methods and event handlers to
the public interface. This makes misuse of your own assemblies to call your WCF
service inapproriately that much harder to do.
5. Purposeful Confusion
I have also been known to purposely provide inaccurate error messages, config settings,
urls, and a wide variety of other pieces of information to make diagnosing failed
hack attempts that much more difficult. Don't tell them anymore than is absolutely
necessary and attempt to track failed actions. Just because they failed doesn't
mean they didn't get close. Clues as to what types of attempts are being made
can help you refine your security plan.
6. Lock Down Source Control Of Key Assemblies
This is pretty much standard practice. If you have things in your Silverlight or
WCF code you don't want the outside world to see, you should reduce the number
of your own internal software developers that have access to it. Nothing like
a knowledgable disgruntled employee attempting to create issues for you.
Summary
As I mentioned above, nothing is 100% fool proof but I think these suggestions used
in conjunction with one another can go a long way toward securing your applications
or at the very least making them a pain to disect. Of course, there are other
things I do but would prefer not to make them public.
There are a lot of creative and knowledgable people out there. If you are one of
them and would like to contribute additional suggestions, please add your comments
at the bottom of this article.