DOC

Securing Your Silverlight Applications

By Larry Thompson,2014-05-11 17:58
9 views 0
Securing Your Silverlight Applications

Securing Your Silverlight

    Applications

    Josh Twist

    In my role as a consultant with Microsoft Services, I have regular discussions

    with customers and partners about application security. In this article, I’ll

    explore some of the themes that arise in those discussions. In particular, I’ll

    focus on the new challenges programmers face when trying to secure Silverlight

    applications, and I’ll consider where development teams should focus their

    resources.

    This article touches on many technical concepts that you’ll find covered in more

    detail elsewhere (including this magazine). For this reason, I won’t explore

    these topics in great technical depth. Instead, the goal of the article is to

    “connect the dots” and show how you can exploit these concepts to secure your

    applications.

    When planning security for an application, it’s useful to think of three A’s:

    authentication, authorization and audit.

    Authentication is the act of confirming that users are who they claim to be. We usually do this with a user name and password.

    Authorization is the process of confirming that a user, once authenticated, actually has the appropriate permissions to perform a particular action or access

    a particular resource.

    Audit is the act of maintaining a record of activity such that actions and

    requests made upon a system can’t be denied by the user.

    I will focus on the first two, authentication and authorization, in the context of a

    Silverlight application. As this is a Rich Internet Application (RIA), the majority

    of concepts described in this article apply equally to Asynchronous JavaScript

    and XML (AJAX) or other RIA approaches. I’ll also discuss how you can prevent

    unwanted access to your Silverlight application files.

    Topology

    Silverlight is a cross-browser plug-in that leverages many of the graphical

    concepts pioneered by Windows Presentation Foundation (WPF), enabling Web

    developers to create rich user experiences far beyond what’s possible with only

    HTML and JavaScript.

Unlike ASP.NET, Silverlight is a client-side technology, so it runs on users’

    computers. So Silverlight development arguably has more in common with

    Windows Forms or WPF than with ASP.NET. In many ways, this is one of

    Silverlight’s greatest advantages, as it removes many of the problems caused

    by the stateless nature of Web applications. However, because all the UI code

    Services runs on client computers, you can’t trust it anymore.

    Unlike Windows Forms, Silverlight operates within the browser sandbox and has

    a reduced set of capabilities, so it provides an increased degree of security

    (though in Silverlight 4, users can identify certain applications as trusted and

    promote the programs’ privileges to allow COM interop). Because of this,

    Silverlight can’t connect to a database directly, so you must create a layer of

    services that provide access to your data and business logic.

    Typically, you host these services on your Web server, just as you would with

    your ASP.NET Web forms, for example. Given that Silverlight code runs on the

    wrong side of the trust boundary between your servers and the real world (see

    Figure 1), the focus of your team’s effort should always be to secure the

    services.

Figure 1 Silverlight Runs on the Wrong Side of the Trust Boundary

    There’s little point in implementing rigorous security checks within your

    Silverlight code itself. After all, it would be easy for an attacker to do away with

    the Silverlight application altogether and invoke your services directly,

    side-stepping any security measures you implemented. Alternatively, a

    malicious person could use a utility like Silverlight Spy or Debugging Tools for

    Windows to change the behavior of your application at runtime.

This is an important realization—a service can’t know for sure what application

    is invoking it or that the app hasn’t been modified in some way. Therefore your services have to ensure that:

    ? The caller is properly authenticated

    ? The caller is authorized to perform the requested action

    For those reasons, most of this article focuses on how to secure services in a way that’s compatible with Silverlight. Specifically, I’ll consider two different types of services hosted via ASP.NET in Microsoft IIS. The first type, services created using Windows Communication Foundation (WCF), provides a unified programming model for building services. The second, WCF Data Services (formerly ADO.NET Data Services), builds on WCF to let you rapidly expose data using standard HTTP verbs, an approach known as Representational State Transfer (REST).

    Naturally, if security is a concern, it’s always wise to encrypt any communication between clients and servers. The use of HTTPS/SSL encryption is recommended and assumed throughout this article.

    Today, the two most common authentication methods Web developers use on the Microsoft platform are Windows authentication and forms authentication. Windows Authentication

    Windows authentication leverages the Local Security Authority or Active Directory to validate user credentials. This is a big advantage in many scenarios; it means you can centrally manage users with tools already familiar to systems administrators. Windows authentication can use any scheme supported by IIS including basic, digest, integrated authentication (NTLM/Kerberos) and certificates.

    The integrated scheme is the most common choice for use with Windows authentication, because users don’t have to provide their user names and

    passwords a second time. Once a user logs on to Windows, the browser can forward credentials in the form of a token or a handshake that confirms the person’s identity. There are some disadvantages to using integrated

    authentication, because both the client and server need visibility of the user’s

    domain. As a result, it’s best targeted at intranet scenarios. Furthermore, though it works with Microsoft Internet Explorer automatically, other browsers, such as Mozilla Firefox, require additional configuration.

    Both basic and digest authentication typically require users to re-enter their user names and passwords when they initiate a session with your Web site. But because both are part of the HTTP specification, they work in most browsers and even when accessed from outside your organization.

Silverlight leverages the browser for communication, so Windows

    authentication is easy to implement with any of the IIS authentication methods

    just discussed. For a detailed description of how to do so, I recommend reading

    the step-by-step guide “How to: Use basicHttpBinding with Windows Authentication and TransportCredentialOnly in WCF from Windows Forms” at

    msdn.microsoft.com/library/cc949012. This example actually uses a Windows

    Forms test client, but the same approach applies to Silverlight.

    Forms Authentication

    Forms authentication is a mechanism that provides simple support for custom

    authentication in ASP.NET. As such, it’s specific to HTTP, which means it’s also

    easy to use in Silverlight.

    The user enters a user name and password combination, which is submitted to

    the server for verification. The server checks the credentials against a trusted

    data source (often a database of users), and if they’re correct, returns a

    FormsAuthentication cookie. The client then presents this cookie with

    subsequent requests. The cookie is signed and encrypted, so only the server can

    decrypt ita malicious user can neither decrypt nor tamper with it. Exactly how you invoke forms authentication varies depending on how you

    implement your login screen. For example, if you’ve used an ASP.NET Web form

    that redirects to your Silverlight application after the user’s credentials have

    been validated, you probably have no more authentication work to do. The

    cookie already will have been sent to the browser and your Silverlight

    application will continue to use the cookie whenever making a request to that

    domain.

    If, however, you want to implement the login screen inside your Silverlight

    application, you’ll need to create a service that exposes your authentication

    methods and sends the appropriate cookie. Fortunately, ASP.NET already

    provides what you needthe authentication service. You just need to enable it

    in your application. For detailed guidance, I recommend reading “How to: Use

    the ASP.NET Authentication Service to Log In through Silverlight Applications”

    at msdn.microsoft.com/library/dd560704(VS.96).

    Another great feature of ASP.NET authentication is its extensibility. A

    membership provider describes the mechanism by which the user name and

    password are verified. Fortunately, there are a number of membership

    providers available as part of ASP.NET, including one that can use SQL Server

    databases and another that uses Active Directory. However, if a provider that

    meets your requirement isn’t available, it’s straightforward to create a custom

    implementation.

    ASP.NET Authorization

Once your users are authenticated, it’s important to ensure that only they can

    attempt to invoke the services. Both ordinary WCF services and WCF Data Services are represented by a .svc file in ASP.NET applications. In this example, the services are going to be hosted via ASP.NET in IIS, and I’ll demonstrate how

    you can use folders to secure access to the services.

    Securing .svc files this way is a little confusing because, by default, a request for such a file actually skips most of the ASP.NET pipeline, bypassing the authorization modules. As a result, to be able to rely on many ASP.NET features, you’ll have to enable ASP.NET compatibility mode. In any case, the WCF Data Services mandate that you enable it. A simple switch inside your configuration file achieves the task:

    

    

    

    

    

    

    With ASP.NET compatibility enabled, it’s possible to prevent access to unauthenticated users by using the authorization section of a web.config file, also shown in the previous code snippet.

    When using forms authentication, the developer must think carefully about which parts of the site need to be accessible, even to unauthenticated users. For example, if all parts are restricted to authenticated users only, how will an unauthenticated user log in?

    It’s often easiest to create a folder structure that supports your basic

    authorization requirements. In this example, I’ve created a “Secured” folder that contains the MyWcfService.svc and MyWcfDataService.svc files, and I’ve deployed a web.config file. In Figure 2 you can see the folder structure, and the

    previous code snippet shows the contents of the web.config file.

Figure 2 Secured Folder Containing the Web.config File

    Note that the root of the application must have anonymous access allowed, otherwise users won’t be able to reach the login page.

    For sites using Windows authentication, things can be somewhat simpler in this respect, as authentication takes place before the user gets to the resources contained within the application, so there’s no need for a specific login page. Using this approach, it’s actually possible to restrict access to services in a more detailed way, allowing only specific groups of users or roles to access resources. For more information, see “ASP.NET Authorization”

    (msdn.microsoft.com/library/wce3kxhd).

    This example implements authorization somewhat, but folder-level

    authorization alone is far too coarse-grained to rely on for most scenarios. Authorization in WCF Services

    Using the PrincipalPermission attribute is an easy way to demand that an invoker of a Microsoft .NET Framework method be within a specific role. This code sample demonstrates how this might be applied to a ServiceOperation in WCF where the calling user must be part of the “OrderApprovers” role:

    [PrincipalPermission(SecurityAction.Demand, Role = "OrderApprovers")] public void ApproveOrder(int orderId)

    {

     OrderManag-er.ApproveOrder(orderId);

    }

    This is easily implemented in applications that use Windows authentication to leverage the existing facility to create Active Directory groups for organizing users. With applications using forms authentication, it’s possible to leverage another great provider-based feature of ASP.NET: RoleProviders. Again, there

are a number of these available, but if none are suitable, you can implement

    your own.

    Of course, even per-method authorization is rarely enough to meet all your

    security needs, and you can always fall back to writing procedural code inside

    your services as shown in Figure 3.

     Figure 3 Using Procedural Code to Implement Specific Authorization

Public void CancelOrder(int orderId)

    {

     // retrieve order using Entity Framework ObjectContext

     OrdersEntities entities = new OrdersEntities();

     Order orderForProcessing = entities.Orders.Where(o => o.Id ==

     orderId).First();

     if (orderForProcessing.CreatedBy !=

     Thread.CurrentPrincipal.Identity.Name)

     {

     throw new SecurityException(

     "Orders can only be canceled by the user who created them");

     }

     OrderManager.CancelOrder(orderForProcessing);

    }

    WCF is a highly extensible platform, and as with all things in WCF, there are

    many approaches to implementing authorization in your services. Dominick

    Baier and Christian Weyer discussed a number of the possibilities in detail in the

    October 2008 issue of MSDN Magazine. The article, “Authorization in

    WCF-Based Services” (msdn.microsoft.com/magazine/cc948343), even

    ventures into claims-based security, a structured way of organizing the

    authorization in your application.

    Authorization in WCF Data Services

    WCF Data Services, as the name suggests, builds on WCF to provide

    REST-based access to a data sourceperhaps most often a LINQ-to-SQL or

    LINQ-to-Entity Framework data source. In brief, this lets you provide access to

    your data using a URL that maps to the entity sets exposed by your data source

    (an entity set typically maps to a table in your database). Permissions to these

    entity sets can be configured inside the services code-behind file. Figure 4

    shows the content of the MyWcfDataService.svc.cs file.

    Figure 4 A WCF Data Services Code-Behind File with Configuration of

    Entity Set Access Rules

    Public class MyWcfDataService : DataService {

     // This method is called only once to initialize service-wide policies.

     Public static void InitializeService(IDataServiceConfiguration config)

     {

     config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead);

     config.SetEntitySetAccessRule("Products", EntitySetRights.AllRead |

     EntitySetRights.WriteAppend | EntitySetRights.WriteDelete);

     }}

    Here, I’ve given Read permissions over the Orders entity set and configured the Products entity set to allow full reading, the inserting of new records and the deletion of existing records.

    However, because WCF Data Services automatically renders access to your data based on this configuration, you don’t have direct access to the code, so there’s no obvious place to implement any specific authorization logic. WCF Data Services supports interceptors that allow developers to implement logic between the client and the data source. For example, it’s possible to specify a query interceptor that filters the results for a particular entity set. The example in Figure 5 shows two query interceptors added to the MyWcfDataService

    class.

    Figure 5 Query Interceptors in WCF Data Services

[QueryInterceptor("Products")]

    Public Expression> OnQueryProducts() {

     String userName

    =ServiceSecurityContext.Current.PrimaryIdentity.Name;

     return product => product.CreatedBy == userName;

    }

[QueryInterceptor("Orders")]

    Public Expression> OnQueryOrders() {

     bool userInPrivateOrdersRole =

     Thread.CurrentPrincipal.IsInRole("PrivateOrders");

     return order => !order.Private|| userInPowerUserRole; }

The first is applied to the Products entity set and ensures that users can retrieve

    only products created by them. The second ensures that only users in the

    PrivateOrders role can read orders flagged Private.

    Likewise, it’s possible to specify change interceptors that run before an entity is

    inserted, modified or deleted as demonstrated here:

[ChangeInterceptor("Products")]

    public void OnChangeProducts(Product product, UpdateOperations

    operations

    {

     if (product.CreatedBy != Thread.CurrentPrincipal.Identity.Name)

     {

     throw new DataServiceException(

     "Only products created by a user can be deleted by that user");

     }

    }

    On initial viewing, the OnChangeProducts change interceptor in this code

    sample appears to expose a security vulnerability, because the implementation

    relies on data passed from an external source—specifically the “product”

    parameter. But when deleting an entity in WCF Data Services, only an entity key

    is passed from the client to the server. That means the entity itself, in this case

    the Product, has to be fetched again from the database and therefore can be

    trusted.

    However, in the case of an update to an existing entity (for example, when the

    operations parameter equals UpdateOperations.Change), the product

    parameter is the de-serialized entity sent by the client, therefore it can’t be

    trusted. The client application may have been modified to specify the CreatedBy

    property of this particular product to a malicious user’s own identity, thereby

    elevating the usurper’s privileges. That could allow modification of a product by

    an individual who shouldn’t be able to do so. To avoid this, I recommend that

    you re-fetch the original entity from the trusted data source based on the entity

    key alone, as shown in Figure 6.

    Figure 6 A Change Interceptor Preventing Unauthorized Insert, Update

    and Delete Operations

[ChangeInterceptor("Products")]

    Public void OnChangeProducts(Product product, UpdateOperations

    operations)

    {

     if (operations == UpdateOperations.Add)

     {

     product.CreatedBy = Thread.CurrentPrincipal.Identity.Name;

     }

     else if (operations == UpdateOperations.Change)

     {

     Product sourceProduct = this.CurrentDataSource.Products.Where(p =>

     p.Id == product.Id).First();

     if (sourceProduct.CreatedBy !=

    Thread.CurrentPrincipal.Identity.Name)

     {

     throw new DataServiceException(

     "Only records created by a user can be modified by that user");

     }

     }

     else if (operations == UpdateOperations.Delete &&

     product.CreatedBy != Thread.CurrentPrincipal.Identity.Name)

     {

     Throw new DataServiceException(

     "Only records created by a user can be deleted by that user");

     }

    }

    Because this implementation relies so much on the CreatedBy property of the Product entity, it’s critically important that this is enforced in a reliable way from the moment the data is created. Figure 6 also shows how this might be

    achieved by overriding any value passed by the client for an Add operation. Note that as the example currently stands, handling operations of type UpdateOperations.Change wouldn’t be an issue. In Figure 4, the service was

    configured to allow only AllRead, WriteAppend (insert) and WriteDelete actions to occur on the Products entity sets. Therefore, the ChangeInterceptor would never be invoked for a Change operation, as the service would immediately reject any request to modify a Product entity at this endpoint. To enable updates, the call to SetEntitySetAccessRule in Figure 4 would have to include

    WriteMerge, WriteReplace or both.

    Cross-Domain Authentication

    The Silverlight plug-in can make cross-domain HTTP requests. A cross-domain call is an HTTP request made to a domain other than the one from which the Silverlight application was downloaded. The ability to make such calls has traditionally been viewed as a security vulnerability. It would allow a malicious developer to make requests to another site (for example, your online banking site) and automatically forward any cookies associated with that domain. Potentially, this could give the attacker access to another logged-in session within the same browser process.

Report this document

For any questions or suggestions please email
cust-service@docsford.com