Securing the Application

The second layer to which security should be applied is the application itself. Typically, this security includes the concepts of authentication, data protection, and user input.

Authentication

Throughout this book we've highlighted the various ways that a Compact Framework application can connect to back-end data. The primary mechanisms include the following:

  • Invoking SqlClient directly: This option was discussed in Chapter 4 and involves directly connecting to a remote SQL Server using the SqlClient .NET data provider.

  • SQLCE connectivity: Both RDA and merge replication use the SQLCE connectivity architecture described in Chapter 7.

  • XML Web Services: In Chapter 4 we discussed calling XML Web Services from the Compact Framework using the generated proxy class in VS .NET 2003.

  • HTTP Pluggable Protocol: In Chapter 4 we also discussed how developers can make direct calls to Web sites using the HTTP protocol through the classes in System.Net namespace.

In each case, the application may need to provide credentials to the back-end server. In the case where SqlClient is invoked directly, this may simply be the user name and password required by SQL Server or the Windows account credentials, depending on whether Windows authentication is enabled in SQL Server. In the case of SQLCE connectivity, we saw that this may involve sending both the proxy server and IIS authentication credentials, as well as the SQL Server credentials, depending on how both IIS and SQL Server are configured. Finally, in the case of XML Web Services and HTTP, because both techniques make requests to Web servers, the authentication scheme enabled on the particular Web server or proxy server will determine what credentials must be passed.

graphics/key point_icon.gif

In all cases, the best practice, of course, is to prompt the user for the credentials, rather than hard-coding them into the application,[6] and then to create the various connection strings or credential objects in memory as necessary. If credentials do, however, need to be stored on the device (for example, if you don't wish to prompt the user for multiple sets of credentials for the proxy, IIS, and SQL Server, or when authenticating through a VPN), they should be stored in an encrypted fashion, in an encrypted SQLCE database (as described in Chapter 5), in a file encrypted using either a third-party solution such as PDASecure or the CryptoAPI, or even on a storage card that the user is required to insert into the device before running the application.[7]

[6] It should be remembered that hard-coding security information in an assembly is not safe because it is easily viewable with utilities such as MSIL Disassembler (ildasm.exe) and classes in the System.Reflection namespace.

[7] Although you can store credentials in the Windows CE registry, doing so will only make them harder to find and will not actually protect them (remember, obscurity is not security). Since there are no managed classes in the Compact Framework to manipulate the registry, developers will need to make the appropriate OS calls using PInvoke or download a class library written by another developer (several of which can be found on the Web).

NOTE

Compact Framework developers will note that application configuration files and the associated Configuration class in the System.Configuration namespace are not supported. In the desktop Framework, developers often use the Configuration class to retrieve connection strings and other application settings from the XML configuration file; however, a custom class can easily be created to retrieve such settings using the classes in the System.Xml namespace, as discussed in Chapter 3.


Although providing credentials to applications using SqlClient or SQLCE connectivity is fairly straightforward through the use of connection strings and public properties, providing credentials to XML Web Services and directly to Web servers using HTTP requires some custom code.

Using SOAP Headers

In the case of XML Web Services, at the time of this writing, there is no agreed upon industry standard for providing credentials to Web Services, although proposals such as WS-Security from IBM and Microsoft provide the beginnings of standardization in this area (see sidebar titled "WS-Security and More in the WSE"). As a result, most organizations rely on either the authentication provided by the underlying transport (HTTP in most cases) or a custom technique using SOAP headers.

To use custom SOAP headers, the developer of an XML Web Service can simply create a class that inherits from the SoapHeader class in the System.Web.Services.Protocols namespace to expose the credential fields (such as the user account and password) that follow:

WS-Security and More in the WSE

In fact, Microsoft has released an add-on for both the Windows .NET Framework 1.0 and 1.1 called Web Services Enhancements (WSE) 2.0 (formerly known as the WSDK in its technical preview incarnation). This add-on provides advanced Web Services functionality, including support for credentials, digital signatures, encryption, message routing, and attachments. The functionality of the WSE is based on the WS-Security, WS-Routing, WS-Attachments, and DIME specifications jointly developed by Microsoft and IBM. This add-on includes additional classes and is installed on a developer's workstation to be used when building XML Web Services with VS .NET.

When a WSE-enabled Web Service is consumed in VS .NET, the client application must include a reference to the Microsoft.Web.Services.dll assembly. This assembly also includes various dependencies to assemblies and classes that are not included in the Compact Framework (such as System.Configuration.Install.dll and many of the classes in the System.Security.Cryptography namespace). As a result, the long and short of the matter is that the Compact Framework does not, in its initial release anyway, include the necessary client-side assemblies for consuming WSE-enabled Web Services.

There have, however, been several attempts to use WSE-enabled Web Services from the Compact Framework by employing custom SOAP headers and SOAP extensions and invoking the necessary cryptographic functions of the operating system using the PInvoke service.[*]

For more information about WSE, see the link in the "Related Reading" section at the end of this chapter.


[*] See www.learnmobile.net/MobileClient/Tutorials/cfWSE for one attempt at using the beta of the Compact Framework.


Public Class AuthHeader : Inherits SoapHeader
    Public UserAccount As String
    Public Password As String
End Class

The developer can then allow the client to provide the SoapHeader when calling the Web Service by adding a public field to the WebService class and decorating the methods that require the header with the SoapHeaderAttribute, as the skeleton class shown in Listing 9-1 makes clear.

Listing 9-1 Implementing a SOAP Header. This listing shows the skeleton code implemented by a Web Service to require the use of a SOAP header as defined in the previous snippet.
<WebService(Namespace:="http://books.atomicdotnet.com ", _
     Name:="AtomicPublishersService")> _
Public Class AtomicPublishers : Inherits
  System.Web.Services.WebService

    Public sHeader As AuthHeader  'SOAPHeader object

    <WebMethod(EnableSession:=False, _
         Description:="Returns orders that need to be fullfilled", _
         BufferResponse:=True), _
         SoapHeader("sHeader", Direction:=SoapHeaderDirection.In)> _
    Public Function GetOrdersToFullFill() As PubOrders
        ' Do a security check
        Me.Authenticate()

        ' Perform the operation and return data
    End Function

    Private Sub Authenticate()

        ' Authorize the use of this Web Service by pulling
        ' the user account and password out of the SOAP Header
        ' as in sHeader.UserAccount and sHeader.Password and
        ' authenticating against a directory.

        ' Throw an exception is not authenticated as in
        ' Throw New SoapException("Incorrect Credentials ", _
        '  SoapException.ClientFaultCode)
    End Sub

End Class

You'll notice in Listing 9-1 that the AtomicPublishers class includes a public field (sHeader) that represents the SoapHeader class. The single operation (the GetOrdersToFullFill method) then uses the SoapHeaderAttribute to identify the object used to process the header, which is sent only from the client to the server. When the client invokes the method, the sHeader variable is automatically populated by the .NET Framework, and consequently, its UserAccount and Password fields can be inspected in the private Authenticate method and used to perform the authentication and authorization.

Fortunately for Compact Framework developers, if such an XML Web Service must be called, the proxy class created in the SDP will include the SoapHeader class and will allow the credentials to be populated before calling the GetOrdersToFullFill method:


AtomicPublishersService ws = new AtomicPublishersService();
AuthHeader auth = new AuthHeader();
auth.UserAccount = txtUser.Text;
auth.Password = txtPwd.Text;
ws.AuthHeaderValue = auth;
PubOrders po = ws.GetOrdersToFullFill();
Using Web Server Authentication

In a simpler way, classes such as System.Net.WebRequest and the Web Service proxy class (inherited from SoapHttpClientProtocol) expose a Credentials property that can be populated with a NetworkCredential object used to provide credentials to a Web server configured with basic or, in the case of IIS, Windows-integrated security.

NOTE

Windows CE, and thus the Compact Framework, does not support Kerberos, Digest, or client certificate authentication.


To add a NetworkCredential to a WebRequest like that shown in Listing 4-4 where wreq is the WebRequest, the following code could be written:


Dim c As New NetworkCredential("dfoxkc", "notmypassword", "")
wreq.Credentials = c

The third argument to the constructor of the NetworkCredential object is the domain, which must be supplied if the Web server is using Windows-integrated authentication.

Implementing Time-outs

When a Compact Framework application prompts the user for credentials, remember that (as discussed in Chapter 2) by default the application is minimized and not closed when the X button in the upper right-hand corner of the application is tapped. As a result, a Compact Framework application will remain in memory unless it is explicitly closed by the application code or using the memory applet in the device settings or until a soft reset is performed or the operating system needs to free up memory.

In some cases, this behavior is not ideal because a nontrusted user may be able to pick up a device on which the application is still running and then have complete access to the data and functionality using the already entered credentials.

To avoid this situation, developers can include a class like that shown (with some additional enhancement, to be sure) in Listing 9-2, which tracks the inactivity in one or more forms by using a timer.

Listing 9-2 Tracking Inactivity. The class shown in this listing can be used to alert the application to a period of inactivity so that it can reauthenticate the user.
Namespace Atomic.CeUtils

    Public Class Inactivity

        Private Shared _t As Timer
        Private Shared _interval As Integer

        Public Shared Event Inactivated(ByVal interval As Integer)

        Public Shared Property Interval() As Integer
            Get
                Return _interval
            End Get
            Set(ByVal Value As Integer)
                _interval = Value
                _t.Enabled = False
                _t.Interval = _interval * 1000
                _t.Enabled = True
            End Set
        End Property

        Shared Sub New()
            _t = New Timer
            _interval = 600 ' 10 minutes by default
            _t.Interval = _interval * 1000
            AddHandler _t.Tick, AddressOf Inactive
            _t.Enabled = True
        End Sub

        Public Shared Sub RegisterForm(ByVal f As Form)
            ' Hook the events for the control and the form
            Dim c As Control
            For Each c In f.Controls
                AddHandler c.GotFocus, AddressOf WakeUp
            Next
            AddHandler f.Activated, AddressOf WakeUp
        End Sub

        Private Shared Sub WakeUp(ByVal sender As Object, _
         ByVal e As EventArgs)
            ResetTimer()
        End Sub

        Private Shared Sub ResetTimer()
            ' Reset the timer
            _t.Enabled = False
            _t.Enabled = True
        End Sub

        Private Shared Sub Inactive(ByVal sender As Object, _
         ByVal e As EventArgs)
            RaiseEvent Inactivated(_interval)
        End Sub

    End Class

End Namespace

In this case, the Inactivity class simply exposes a RegisterForm static method that accepts a Form object and registers handlers for the GotFocus events of all of its controls, as well as the Activated event for the form itself. When one of these events fires, the timer is reset (the setting defaults to ten minutes, but is configurable through the Interval property). In the event that the form (or forms) is inactive for the specified amount of time, the class raises an event, which can then be caught in the application and used to prompt the user for login credentials.

A form in an application can then use the class like so:


Atomic.CeUtils.Inactivity.RegisterForm(Me)

AddHandler Atomic.CeUtils.Inactivity.Inactivated, _
  AddressOf ReAuthenticate

Data Protection

The second major task in securing an application is to protect the data it uses. As discussed in Chapters 3 and 5, the two major forms of data used by Compact Framework applications are local files (typically in XML format) stored in RAM or on a storage card on the device and SQLCE. In order to secure the data in both cases, encryption should be used.

NOTE

Keep in mind that perhaps the simplest form of data protection is to use the backup and restore facilities provided by ActiveSync. These facilities allow users to perform full and incremental backups and restore the backup onto the device.


SQLCE Encryption

As discussed in Chapter 5, SQLCE allows databases (.sdf files) to be password protected and optionally to be encrypted using the symmetric RC4 algorithm with a key generated from an MD5 hashed password.

Using this approach, all of the data retrieved from a remote SQL Server using RDA or merge replication can be safely stored on the device. An application can then prompt for the password and use it in the connection string to open the database. Be aware of the following issues, however:

  • The password is not recoverable. The database password cannot be regenerated if lost, so the application will need to be able to recreate the data (for example, through a synchronization) in the event the password is lost and not recoverable.

  • The password can be changed only by compacting the database. This means that applications will need to include the code required to compact the database as shown in Chapter 5 and that the device will need enough free space to handle the compaction.

  • The strength of the encryption depends on the length of the password. Because the key the RC4 algorithm uses is a hash of the password, the length of the password determines the strength of the encryption. The application should enforce at least a minimal (eight-character or longer) password that combines letters, numbers, and special characters.

  • You can create single password applications. One simple approach is to use SQLCE encryption as the gatekeeper for the entire application. In other words, the application prompts the user for the password and then retrieves the other required credentials (for IIS, SQL Server, and so on) from a settings table within SQLCE. In this way the user needs only a single password, and other credentials are safely stored on the device.

File Encryption

When data is stored in XML files or some other file format on the device or on a storage card, it should be protected via encryption.

As mentioned in Chapter 1, the .NET Framework supports a robust set of encryption classes in the System.Security.Cryptography namespace, as discussed in the article referenced in the "Related Reading" section at the end of the chapter. These classes support both symmetric (such as DES, Triple-DES, and RC2) and asymmetric (such as RSA and DSA) algorithms, as well as hashing (MD5, SHA1, SHA512, among others) and random-number generation; however, they are not supported in the Compact Framework. The underlying operating systems, such as Windows CE, may, however, provide system APIs to perform encryption. In fact, Windows CE does include a subset of the CryptoAPI, which is callable using PInvoke.[8]

[8] Some of the CryptoAPIs, however, require callbacks, which are not supported in the Compact Framework.

It is also worth noting that when using the CryptoAPI on Pocket PC 2000, only 40-bit encryption is supported out of the box. In order to use 128-bit encryption, the Microsoft High Encryption Pack can be downloaded.[9] Pocket PC 2002 devices already include this functionality. There are also several code libraries, such as the one offered by Certicom Corporation, that include encryption functionality that can be integrated into an application.

[9] See www.microsoft.com/mobile/pocketpc/downloads/ssl128.asp.

A simpler, and more global, technique might be to use one of the various third-party solutions. For example, Casio provides storage cards with built-in hardware encryption. By storing files or even a SQLCE database on such a card, the application can offload the responsibility for data security. In addition, vendors such as Trust Digital, through its PDASecure product, provide file-based encryption (available in a number of different algorithms) tied to the power-on password authentication discussed earlier. For example, the PDASecure application provides a dialog like that shown in Figure 9-2 for encrypting selected files on the device.

Figure 9-2. Using File Encryption. This screen shot shows the interface used by PDASecure to encrypt files stored on a Pocket PC.

graphics/09fig02.gif

Securing User Input

graphics/key point_icon.gif

The final issue to consider when dealing with application security is securing user input as codified in the "Don't trust user input" principle discussed earlier. As discussed in Chapter 7, this issue is most directly relevant when accepting user input that is subsequently used to query or update data in SQLCE or on a remote SQL Server. Malicious users may attempt to insert, or inject, additional SQL into a TextBox control, thereby causing a possibly harmful statement to run against the database. These SQL injection attacks can be mitigated by cleansing what the user enters with a function like that shown in the following snippet or using parameterized queries:


private string SanitizeIntoSqlLiteral(string strQ)
{
   //Convert single quote into two single quotes
   return strQ.Replace("' ", "'' ");
}

In general, user input can be checked using regular expressions through the RegEx class of the System.Text.RegularExpressions namespace. One interesting use of this class is to create a derived TextBox control that uses a regular expression to validate the text entered by the user, as shown in Listing 9-3.

Listing 9-3 Validating User Input. This derived TextBox control uses the RegEx class to validate the input using a regular expression.
Public Class PatternTextBox : Inherits TextBox

    Private _r As Regex

    Public Sub New()
        _r = New Regex(String.Empty)
    End Sub

    Public Sub New(ByVal expression As String)
        _r = New Regex(expression)
    End Sub

    Public Property Expression() As String
        Get
            Return _r.ToString
        End Get
        Set(ByVal Value As String)
            _r = New Regex(Value)
        End Set
    End Property

    Private Sub PatternTextBox_Validating(ByVal sender As Object, _
     ByVal e As System.ComponentModel.CancelEventArgs) _
     Handles MyBase.Validating

        If Me.Text = String.Empty Then
            Return
        End If

        If Not _r.IsMatch(Me.Text) Then
            e.Cancel = True
            Me.Select(0, Me.Text.Length)
            Me.Focus()
        End If

    End Sub
End Class

In Listing 9-3 the PatternTextBox class inherits from TextBox and exposes the Expression public property used to hold the regular expression. The Validating event is then hooked by the PatternTextBox_Validating method and there determines whether the text in the control matches the regular expression. If it doesn't, the text in the control is selected, and focus is not allowed to leave the control.

A client could then create an instance of the PatternTextBox control, placing it on the form and ensuring that the user enters a valid address:


PatternTextBox p =
 new PatternTextBox("^\w+[\w-\.]*\@\w+((-\w+)|(\w*))\.[a-z]{2,3}$");
this.Controls.Add(p);

TIP

Obviously, an interesting enhancement to this control would be to preload it with a variety of regular expressions to handle common types of input, including currency, dates and times, social security numbers, and telephone numbers.


What about CAS?

As many .NET Framework developers are no doubt aware, the .NET Framework includes an enhanced security system called Code Access Security (CAS). CAS provides a security model that is a step beyond the traditional identity-based model (where permissions for running code are derived from the identity of the account under which the code is currently executing) by allowing assemblies to be assigned varying levels of trust based on attributes of the code itself. These attributes can include, but are not limited to, a digital certificate, the originating site or URL, the application directory, the hash value of the assembly, or the strong name.

CAS then includes a series of permissions (defined as classes in the Framework) that are grouped into permission sets and then assigned to code based on code groups, which define the intersection of permission sets and evidence. Security policies then define the various code groups and permission sets at the user, machine, and enterprise levels. CAS configuration can be set using the Microsoft .NET Framework Configuration tool installed in the Administrative Tools group. For more information on CAS, see the articles in the "Related Reading" section at the end of this chapter.

Although this model can be very effective for protecting a user's machine and yet allowing an application to execute with the permissions it needs, especially for code in smart client applications downloaded from the Web, the benefits of CAS are not as transferable to smart devices. As a result, the full implementation of CAS in the Compact Framework is not included in the initial release. The Compact Framework does, however, support all of the infrastructure necessary to implement security policy and code groups. It is just that in the initial release, security policy cannot be edited, and all code runs in the All_Code code group associated with the FullTrust permission set and therefore has full access to the device.

In addition, because smart device operating systems are by nature single user, the Compact Framework also does not support the identity and principal classes (the latter implementing the IPrincipal interface) and the associated role-based security mechanisms, as does the .NET Framework. These classes are used to identify the current user and determine whether that user is in a particular role.