Sandrino Di Mattia

Content-type: Microsoft

Social

Follow me on Twitter! LinkedIn! RSS! SilverlightShow Contributor

Recent posts

Tags

Categories

Archive

Blogroll

Cracking a Microsoft contest or why Silverlight-WCF security is important

A few days ago Microsoft Belgium did something great for the community; a contest! The prizes for this contest include Windows Phones, TechNet subscriptions, books...

The concept is simple: make sure you register for the MSDN/TechNet newsflash and play the online game (Mastermind look-a-like). If you subscribe to the newsletter you'll get an email every 2 weeks that will reveal a color. Each of these colors you can exclude while playing, since they'll never be part of the solution. This means that reading the newsletter can help you score better. And the only way to score is by solving the puzzle - but you also need to have the best time.

Also important to know is that only the best 20 times will win a great prize (like a Windows Phone 7 or a laptop). And to keep things interesting the high scores don't show the top 20 scores.

Now there I was playing the game while I was debugging an application with Fiddler when I noticed something... the Silverlight was communicating with a WCF service. Nothing special here, were it not for the fact that after some time I was able to access the list of all high scores, insert my own highscore (which could make me win a Windows Phone or a laptop), ...

After doing a few tests I notified Microsoft Belgium, but I guess someone already took advantage of this 'exploit'. The top score is someone who solved the puzzle in a little over 2 sec... sure! Even if you tweak your mouse and have loads of luck it's not possible to drag the 4 balls that quickly. But this is as much the fault of the people who cheated as it is the fault of the people who created this game.

The reason why I'm writing this article is because I find it frustrating that people don't always take security seriously. This is a game with cool prizes and this means people will try to cheat, so don't make them. Make sure the contest is cheat-proof so everyone can play the game and win the prizes in an honest way. Because in the end it only takes 1 person to ruin the game and a few days after the start of the contest this already happened. Even if security isn't always a functional requirement, don't ignore it!

I've also been wanting to write an article on security for some time now and this seems the perfect case. Hopefully Microsoft Belgium will fix the security leaks, reset the score and give people the chance to really play! And for the honest people who reached the top scores, if you did it before I'm sure you'll do it again. But more important I hope people will learn the possible ways your applications can be abused and how you can fix it. This is really important for your internet facing deployments.

See update at the end of the article: There seems to be a server-side anti-cheating mechanism acthive so there is no need to re-initialize the highscores.

Now, let's get started...

Intercepting the webservice calls

Like I was saying before, I stumbled upon these webservice calls while I was debugging an other project with Fiddler. Fiddler is this great tool that will nest itself as proxy between your applications and the internet. It can also monitor webservice calls and supports decoding of HTTPS sessions.

For example, when I click the highscores button of the game a webservice request is made to SilverlightService.svc. As you can see in the red rectangle it seems a method GetHighscores was called. If you already used Fiddler before (and I really hope you did) you'll maybe find it strange that you're not seeing the real XML that was sent to the server. This is because the binding on the service side is configured using binary message encoding. 

To decode these binary messages you'll need a nifty plugin for Fiddler: WCF Binary-encoded Message Inspector for Fiddler This plugin will decode the binary messages to clean SOAP.

Now as you can see the GetHighscores method allows you to specify a minimium and maximum rank. Does this mean I could also get the top 20 high scores? Maybe...

Problem: Webservice calls can be intercepted.
Solution: Well there isn't much you can do to prevent people from looking at the webservice calls. Just make sure you send as little as possible over the wire! And a little HTTPS wouldn't hurt either (even though you can still decode it with Fiddler).

Connecting to the webservice

Now that we know the address of the webservice we could try to connect. Just copy the URL, create a Console application in Visual Studio 2010 and add a Service Reference. If everything goes well the code generation should start to kick in and all service methods will be available through a service client.

After testing all these methods it looks like I can use 2 of them without any problem. CreateEntry seems to create a new game but that's it - this seems to be safe. But then there's the GetHighscores method... very disappointing! Without any hacking or cracking I can call this method with min. value 0 and max. value 20 and this returns the top 20 scores. Besides that I also have the full names and the email adresses of the people who played.

Problem 1: Requesting all high scores, even the secret ones. If the top scores should be kept secret until the end, why still expose them using the GetHighscores method? The Silverlight application doesn't use them so you are exposing more data than required.
Solution: Only implement methods and expose data that will actually be used by the Silverlight application. A better option would have been to create a parameter-less GetHighscores method. Since the top scores should only be available mid january one could have added some logic on the service side to only show the secret high scores if required.

Problem 2: This is actually the same problem... exposing too much data. The email addresses of the players are not displayed in the application so why expose them in the service? Don't expose data if you don't use it! This can have serious consequences. What if there were money prizes involved and I had to give up my address and/or bank account? I wouldn't want anyone to be able to access this data.
Solution: Only expose the non-confidential data that is required by the application.

Problem 3: Anonymous access. I don't understand why anyone could just access this service without any security?
Solution: Take a look at security options like Windows Live ID, Windows Azure, cookies or even custom message headers. Beware that most of these options can be 'bypassed', in this case it's not even relevant, but some security is always better than no security at all.

Problem 4 (Steve Degosserie): Anyone can generate a service proxy by adding a service reference to the url.
Solution: Disable the metadata so you can't use a service reference. This can be configured in the web.config (look for MEX/Metadata exchange). Do note that this isn't enough; if the generated code is available in the Silverlight assemblies (see below) anyone can still use the service. This just makes it harder.

Decompiling the Silverlight application

What is a Silverlight application? A Silverlight application is actually an XAP file displayed on a webpage (or OOB, ... but this isn't relevant). When you take a look at the source of a page (for example right click the page, View Source) you can find the path to the XAP file. In this case just looking at the source of the page won't cut it. I didn't bother to search the javascript files for the link so I started Fiddler again.

If you navigate to the website Fiddler will show you all resources being downloaded, including .css files, .js files but also the .xap file. Then you can just copy the URL to the .xap file. When everything pops up correcty - besides the .xap file this means it was cached from a previous visit. Try clearing your temporary internet files or use InPrivate browsing to make the URL show up again. Alternatively you could search for the .xap in your temporary internet files.



If you download this file and open it with 7zip for example you'll see the contents of the Silverlight application:

For this case I had to analyze the msbe155.Silverlight.dll assembly. Using Reflector I was able to decompile the assembly and take a look at the code and the resources.
Let's start with the resources. Just by taking a look at the resources - based on the filenames - I already spotted the colors that were marked as excluded.

Now to be sure I'm not running on a hunch I decided to go and look in the code. Since Reflector allows you to decompile each class in each assembly I stumbled on the following code:

With a bit more effort I was able to gain access to all the resources and all the code, including some logic that could work to my advantage.

Problem: Secret business logic was discovered while decompiling the code and much of the processing is done on the client.
Solution: This kind of business logic should not be on the client but on the service (even if this wouldn't be the best solution for this case). If you do decide to keep the logic on the client you should make sure not everyone can look into it (more on this later).

Cracking the security

We haven't done anything too evil until now. Finding some excluded colors or accessing the secret top scores is still semi-evil. But what if I'm really evil and I want to cheat? I could try to add a highscore with my name and make sure this score has the best time. The best time would result in me being at rank #1.

When I saw the method SaveHighscore I was again very disappointed but this feeling went away rather quickly. The SaveHighscore method accepts the Highscore class but hold your horses! It looks like I can't just make up every property! The following fields look like they implement some fancy logic to prevent me to add a random highscore!

The fields I'm talking about are:

  • Guid
  • Hash
  • SecurityHash

There you got it! A SecurityHash! Now I can just give up... nobody can cheat here. Let's play a bit and then get back to work. Wrong! I still had the assembly open in Reflector and after a few minutes I found a block of code that showed me how to build a Highscore object, including the security hash.

Without too much effort I now had the code required to position myself at rank #1. This could be a real cash cow for me; I could add some friends and family on the following ranks and get all of them sweet Windows Phones! Having this code was great but it didn't seem to work. After all it looked like I was missing something from the original game; some cookies seemed to be required for everything to work correctly.

Finally I had to write a small MessageInspector for the outgoing WCF calls and an EndpointBehavior that used this MessageInspector to get the cookies working correctly and poof... the highscore was added.

Problem: Custom security logic can be decompiled and can be used to exploit the system.
Solution: Adapt your application to have all the business logic on the server. In this case this isn't an option because the scores are based on time and too many service calls would just slow things down. A better solution for internet facing applications is obfuscation. Invest in a product like Dotfuscator and you won't be sorry! It supports code encryption, XAML encryption, string encryption and so much more! Don't take the risk of people decompiling your code and finding all your secret business logic.

Conclusion

Maybe I took "KRAAK DE CODE" (crack the code) a little too serious but hopefully this case will help you understand that security is an important part of our daily routines as developers and we are responsible for the applications we build. And even if you're not an expert on security there are many good resources available on the net: just take a look at Channel9, Windows Identity Foundation, Azure AppFabric ACS, books by Juval Lowy on WCF, ...

I will post the MessageInspector, EndpointBehavior and the rest of the working solution after the contest.

Enjoy

Update 1: Added remark by Steve Degosserie to 'Connecting to the webservice'.

Update 2: I'm happy to announce I just received a call form Arlindo (MSFT) about this article and he assured me that cheaters can and will be detected. I added several records in the high scores (with 2 other names besides my own) and he assured me they were able to detect all my cheating attempts. Besides that they also detected cheat attempts by other players, the 2sec. guy for example. Cheaters are still visible in the highscores but they will be removed manually. The developers are also working on the service to make sure the email addresses remain private. And maybe after the contest they will communicate how the anti-cheating mechanism works. 

In any case I'm very pleased with Microsoft Belgium's openness, quick response and the fact that they didn't want me to delete this article. Thanks!

Posted: Dec 23 2010, 09:12 by Sandrino | Comments (3) RSS comment feed |
Filed under: .NET | Silverlight | WCF

Making WCF RIA Services work in a DMZ/Multitier architecture using Application Request Routing

Introduction

In large companies / governments / ... most of the time the application architecture needs to follow a set of rules (focused on maintainability and security).
These could be rules like the following:

  • The applications need to be developed following a Multitier architecture
  • Each tier should be physically separated for security.
  • The business logic tier is the only one that can connect to the data tier.
  • The presentation tier only connects to the business logic tier.
  • The presentation tier may not directly connect to the data tier.
  • The presentation tier is located in a DMZ, other tiers are heavily secured.
  • ...

Each project / company will have its own rules but the concept stays the same.
Here is an example of how this could be achieved in ASP.NET:

As you can see, for the data tier to be compromised one must first compromise the presentation and the business logic tier.
You can also assume that in most cases each physical tier is also protected by a firewall.

Default: Physical tiers in Silverlight + WCF RIA Services

When you're building an application in Silverlight with WCF RIA Services you'll get the following setup:

The setup is still a 3-tier achitecture, but the presentation tier runs on the client.
You could argue if the middle tier does or does not count as a presentation tier (since everything runs on the client) but let's say it does.

If you look at this from a security point of view it's less safer.
Once the webserver in the middle is compromised one has direct access to the data tier.

We could just add an extra tier containing some WCF Services that would be consumed by our WCF RIA Services but that would cause code duplication.
This would be an undesired side effect and thus we won't see this as a good solution to our problem.

Separate: An extra Web Application for WCF RIA Services

In my last post (Things you can do with WCF RIA Services and a regular .svc file) I described how you could separate the web application (hosting Silverlight) and the services. Resulting in the following setup:

 

As you can see here we're a step closer. Our services can live on one server and our web page (hosting the Silverlight application) on an other server. This does separate our tiers physically. But the problem is that Silverlight still requires a connection to WCF RIA Services directly.

In this setup our Business Logic Tier should be exposed to the internet / be in a DMZ / ... for our Silverlight client to access it.
And again, if this server is compromised one has direct access to the Data Tier!

Advanced: Using IIS Application Request Routing

For more information about ARR please visit: http://www.iis.net/download/ApplicationRequestRouting

We'll be using ARR (in combination with URL Rewrites) because it allows us to use IIS as a reverse proxy.
Using this reverse proxy we'll be able to achieve the following setup:

Follow these steps to install and configure IIS Application Request Routing.

A. Preparing the server(s)

  1. Make sure you have IIS 7 or IIS 7.5
  2. Download the Application Request Routing extension: x86 / x64
  3. Install the extension (it might install other extensions first)
     
  4. For testing purposes we'll simulate 2 servers on one machine.
    To do this, open c:\windows\system32\drivers\etc\hosts with notepad.

    127.0.0.1 presentationtier
    127.0.0.1 logictier


    We'll link these hostnames using host headers in IIS.

    If you have 2 servers you can use for testing, please do (and you can skip this step).
    But don't forget to install the WCF RIA Services Toolkit!
     
  5. Finally we'll configure the Application Request Routing.
    In IIS Manager, click your server and go to Application Request Routing Cache:



    Important note: If you work with multiple servers (and that's what you'll do in a real environment), you need to do this on your presentation tier (the ASP.NET website containing the Silverlight application).

  6. On the right select Server Proxy Settings and check the box Enable proxy.

B. Creating the site for the business logic tier

  1. In IIS, create a website called LogicSite
     

     
    Note that the 'Host name' points to logictier under Binding.
    This way we can simulate that this website is configured on the server logictier.
     
  2. Now go to Application Pools and open the LogicSite application pool.
    Change the .NET Framework version to v4.0.

C. Provisioning the business logic tier site

Splitting up a Silverlight+RIA Services application requires some actions and you can read all about it in my last article.
We'll be using the solution from that article to get started right away. Note that this solution does not connect to a data tier, it just simulates this using a static list.

  1. Download the complete solution and extract it: Sandworks.Silverlight.nTier.zip (464.99 kb)
  2. Open the solution using Visual Studio
  3. Right click the project and select publish.
     

     
  4. Point the publish to the correct directory.
     

      
  5. Press Publish.
  6. Now visit http://logictier/Tasks.svc and your WCF RIA Service should be working correctly.

  7. Now, create a file called clientaccesspolicy.xml in the root of this site containing the following XML (required for cross site access in Silverlight):

    <?xml version="1.0" encoding="utf-8"?>
    <access-policy>
      <cross-domain-access>
        <policy>
          <allow-from http-request-headers="*">
            <domain uri="http://*" />
            <domain uri="https://*" />
          </allow-from>
          <grant-to>
            <resource path="/" include-subpaths="true"/>
          </grant-to>
        </policy>
      </cross-domain-access>
    </access-policy>

That does it. Our business logic tier (on a 'separate server') has been configured and is working.

D. Creating the site for the presentation tier (and reverse proxy)

  1. In IIS, create a site called PresentationSite

    Note that the 'Host name' points to presentationtier under Binding.
    This way we can simulate that this website is configured on the server presentationtier.
     
  2. Now go to Application Pools and open the LogicSite application pool.
    Change the .NET Framework version to v4.0.
     
  3. For this application pool go to Advanced Settings and change the Idle-Time out to 0 minutes.
    And finally go to Recycle... and clear the Regular time intervals (in minutes) checkbox.

E. Provisioning the presentation tier site

  1. Go back to Visual Studio, to the Sandworks.Silverlight.nTier.Client project.
  2. Open MainPage.xaml.cs
  3. Change both links you see there to http://logictier/Tasks.svc
  4. Rebuild the complete solution.
  5. Publish the Sandworks.Silverlight.nTier.Web site to our PresentationSite.
     

     
  6. Now, open the following link: http://presentationtier/Sandworks.Silverlight.nTier.ClientTestPage.aspx
  7. If everything goes well you should see a Silverlight application, and when you press the button Get all tasks you'll see this:
     

Now you've got a fictive server running the services and an other server running the actual web application hosting the Silverlight application.
The Silverlight application runs locally but still connects to the business logic tier. Now we created a setup as described in Separate (WCF RIA Services split in 2 servers).

Let's continue.

F. Configuring IIS Application Request Routing and IIS Rewrite

  1. Open the web.config of the PresentationSite (running on the presentation tier).
     
  2. Now add the following to the configuration file:

     <system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
          multipleSiteBindingsEnabled="true" />
      </system.serviceModel> 
      <system.webServer>
        <validation validateIntegratedModeConfiguration="false" />
        <rewrite>
          <rules>
            <rule name="Reverse Proxy to Business Logic Tier" stopProcessing="true">
              <match url="^riaservices/(.*)" />
              <action type="Rewrite" url="
    http://logictier/{R:1}" />
            </rule>
          </rules>
        </rewrite>
      </system.webServer>


    This will make sure all requests to the path riaservices are forwarded to our logictier server (containing the business logic tier).
     
  3. Now, visit the page: http://presentationtier/riaservices/Tasks.svc

And there you have it. Even tough we're visiting a page on the server presentationtier it's showing us content from the logictier server.
This means our Silverlight application no longer needs to talk to the logictier server. And thus, we don't need to expose our logictier server to the internet or put it in a DMZ.

Note 1: The clientaccesspolicy.xml file we placed in the LogicSite is no longer required.
Note 2: The system.serviceModel part in the config is very important. If it's missing you'll get the following error:

In your browser:

Server Error in '/' Application.
The resource cannot be found.

In EventViewer:

WebHost failed to process a request.
 Sender Information: System.ServiceModel.Activation.HostedHttpRequestAsyncResult/27111447
 Exception: System.Web.HttpException (0x80004005): The service '/riaservices/Tasks.svc' does not exist. ---> System.ServiceModel.EndpointNotFoundException: The service '/riaservices/Tasks.svc' does not exist.

G. The final result in our Silverlight application

  1. Go back to Visual Studio.
  2. Open MainPage.xaml.cs
  3. Change both urls to http://presentationtier/riaservices/Tasks.svc
  4. Rebuild the complete solution.
  5. Publish the Silverlight application like we did in E-5
  6. Check the web.config if it still contains the URL Rewrite configuration.
  7. Visit http://presentationtier/Sandworks.Silverlight.nTier.ClientTestPage.aspx 

And we're done...

If you want you can start Fiddler and you'll see that our Silverlight application is only accessing our presentationtier server:

After a very long article this is what we've accomplished:

 

Our solution is ready for the enterprise!

Downloads:

Enjoy..

Things you can do with WCF RIA Services and a regular .svc file

Introduction

Let's say you create a new DomainService called TasksDomainService and host this service in an ASP.NET website.

The HttpModule DomainServiceModule will make sure you can access this service as if a physical .svc file was present on the webserver. It will take the namespace and the class name (in my case Sandworks.Silverlight.nTier.Services and TasksDomainService), combine them and append ".svc".

In my case I could access the service via: http://localhost:9346/Sandworks-Silverlight-nTier-Services-TasksDomainService.svc

Custom .svc file

But what to do when you want a cleaner url (http://localhost/Tasks.svc for example)?
This is possible, just follow these steps:

  1. In the web project containing your service, create a new file: Tasks.svc
  2. Open this file
  3. Enter the following:
     
    <%@ ServiceHost Service="Sandworks.Silverlight.nTier.Services.TasksDomainService" Factory="System.ServiceModel.DomainServices.Hosting.DomainServiceHostFactory, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

The markup you see in step 3 tells the server what service to load (Service, this needs to include the namespace) and how to load it (Factory, the DomainServiceHostFactory will correctly host your DomainService class).
If you don't use the fully qualified assembly name this will result in the following error (thanks Luc!):

Parser Error Message: The CLR Type 'System.ServiceModel.DomainServices.Hosting.DomainServiceHostFactory' could not be loaded during service compilation. Verify that this type is either defined in a source file located in the application's \\App_Code directory, contained in a compiled assembly located in the application's \\bin directory, or present in an assembly installed in the Global Assembly Cache. Note that the type name is case-sensitive and that the directories such as \\App_Code and \\bin must be located in the application's root directory and cannot be nested in subdirectories.

Do read Rick's post if you also want to remove the .svc extension to go RESTfull: http://www.west-wind.com/weblog/posts/570695.aspx

Clean project structure

When creating a new solution I love to organize a clean project structure.
But when you're using Silverlight with WCF RIA Services this can be challenging though.

This is what we'll try to accomplish:

A little word on the projects:

  • Sandworks.Silverlight.nTier.Client: The Silverlight application.
  • Sandworks.Silverlight.nTier.Client.Model: A regular Silverlight Class Library with a WCF RIA Services link.
  • Sandworks.Silverlight.nTier.Services: A WCF Service Library.
  • Sandworks.Silverlight.nTier.Services.Host: A WCF Service Application.
  • Sandworks.Silverlight.nTier.Web: This hosts the Silverlight application.

Note, we won't be using a WCF RIA Services Class Library because the namespaces and assembly names are not so clean (.Web is appended to your server side class library):

 

Here are the steps you need to follow to create your own clean project structure:

  1. Open Visual Studio, select File, New Project
  2. Select Silverlight Application
  3. Enter the following:
     
    Name: My.Application.Client
    Solution: My.Application
     
  4. Visual Studio will ask you to host the application in a new ASP.NET Web Site.
    Make sure you change My.Application.Client.Web to My.Application.Web and press OK (do not check Enable WCF RIA Services).
  5. Add a new Silverlight Class Library called My.Application.Client.Model and remove Class1.cs
  6. In your project My.Application.Client add a project reference to My.Application.Client.Model
  7. Add a new WCF Service Library called My.Application.Services and remove all files.
  8. Add a new WCF Service Application called My.Application.Services.Host and remove the fake Service1.
  9. In your project My.Application.Services.Host add the following references:
     
    My.Application.Services
    (project reference)
    System.ComponentModel.DataAnnotations
    System.ServiceModel.DomainServices.Hosting

    System.ServiceModel.DomainServices.Server
     
  10. Rebuild your solution.
  11. In your project My.Application.Client.Model go to Properties and add a WCF RIA Services link to My.Application.Services
    This will make sure the code generation is in place.
  12. Create 3 solution folders: Client, Services, Web and reorganize your projects.

And you're done:

The only things left to do are:

  1. Create a new Domain Service Class in My.Application.Services
  2. Create a .svc file in My.Application.Services.Host (referencing that class, see start of this post)
  3. Rebuild your solution (code generation will take place in My.Application.Client.Model)
  4. Use your service context in My.Application.Client
  5. Configure the web applications on a fixed port (easier for when you're debugging).

Since your service isn't in the same project/Virtual Directory as the Web project you'll have to explicitly declare the url:

TasksDomainContext ctx = new TasksDomainContext(new Uri("http://localhost:10001/Tasks.svc"));

In a following article I'll explain how to configure these urls.
But for now here are the downloads:

Enjoy...