Deployment Security


When designing, developing, and deploying a SQL-NS application, you should assume that at some time, a malicious user will try to find and exploit security vulnerabilities in it. Simply stated, your job is to prevent such a user from succeeding in this endeavor. You do this by eliminating security vulnerabilities wherever possible and configuring the system so that any potential vulnerability will provide only minimal access to the resources on the system.

This section describes some of the SQL-NS features that can be used to implement a secure deployment and outlines some security basics that apply to most deployment environments. The SQL-NS platform was designed with security in mind, but all the SQL-NS security features (as well as the security features of SQL Server and Windows) are just tools; they must be applied appropriately to be effective. The information presented here is intended only as a guide. Ultimately, the responsibility is yours to design and implement the appropriate security measures for your particular deployment environment.

Caution

When designing a security policy, it's easy to miss things, despite thinking carefully and methodically. Also, security threats and their recommended countermeasures are constantly evolving. Security policies should always undergo regular review and testing to ensure that they meet the demands of your environment.


Security Basics for the Deployment Environment

This section describes some of the recommended security practices for SQL-NS deployments. The information in this section builds on the security concepts for development environments covered earlier in this book.

Accounts and Authentication

As described in Chapter 2, "Getting Set Up," SQL-NS supports both Windows Authentication and SQL Server Authentication. Although both methods are supported, Windows Authentication is recommended because it is more secure and generally simplifies development and administration.

All running components of a SQL-NS instance (the SQL-NS engine, the SMI, and stand-alone event providers) run under the credentials of some Windows account. It's recommended that you use domain accounts to run these components and grant those accounts only the minimum permissions they need to carry out their functions. In the development environment, we used the same account to run all the components, but in deployment, it's a good idea to use different accounts for the different components. Doing this allows you to assign each account the smallest possible set of permissions. This minimizes the access that an unauthorized user will be able to gain, should any of the components be compromised.

All accounts (Windows accounts, as well as any SQL Server accounts used for SQL Server Authentication) should be configured with strong passwords. You can find guidelines for creating strong passwords on Microsoft's Trustworthy Computing website at http://www.microsoft.com/security/articles/password.asp.

Guarding Sensitive Information

In SQL-NS instances, sensitive information may exist in several places, including

  • Arguments to custom components specified in the ICF and ADFs

  • Code that uses SQL usernames and passwords to connect to the database using SQL Server Authentication

  • Environment variables used in scripts that invoke nscontrol commands

This section describes some techniques that can be used to guard this sensitive information from unauthorized access.

Arguments to custom components are read from the ICF and ADFs and are stored in instance and application database tables. Although the ICF and ADFs must themselves be secured (the next section provides information on this), the arguments in the database should also be protected using the argument encryption feature of SQL-NS. For detailed information on argument encryption, see the "Argument Encryption" section (p. 237) in Chapter 8, "Event Providers."

If you use SQL Server Authentication, you need to supply SQL usernames and passwords to various APIs in the implementation of SMIs and standalone event providers. For example, NSInstance objects require their SQL username and password properties to be set before they can connect to the instance database via SQL Server authentication. In the examples shown in this book, the SQL usernames and passwords were hard-coded in the source files. This was done for simplicity and ease of explanation but is not a secure practice. Anyone with access to the source code will be able to view the passwords, and anyone able to attach a debugger to the process running the code may be able to extract the passwords from the process's memory. Furthermore, hard-coding passwords presents difficulties for administrators because the code must be recompiled if the usernames or passwords change.

You should avoid hard-coding passwords in source files whenever possible. Alternatives are to store passwords in protected configuration files or Registry keys. But regardless of how secure the storage location may be, passwords should never be stored in plain text. Instead, they should always be encrypted using the Windows Data Protection API (see http://msdn.microsoft.com/library/en-us/dnsecure/html/windataprotection-dpapi.asp?frame=true for more information on this API). At runtime, components should read the encrypted passwords from the stored locations, decrypt them, and pass them to the APIs that require them.

Caution

It's especially important not to hard-code passwords in source files for web applications stored on web servers. Even with the appropriate security settings in place to deny web browsers access to the parts of the web server containing source code, the risk is not eliminated. Hackers are constantly finding security holes in web servers that allow them to view web application source code.


Scripts that invoke nscontrol commands may temporarily store sensitive information in environment variables. For example, in the scripts used with the samples in this book, the value for the -sqlpassword argument to nscontrol create is stored in and read from an environment variable. All this book's scripts clear out environment variables containing sensitive information before they exit. Sensitive information can remain behind in memory if a script does not clean up properly. In any scripts that you write, be careful to clear out any environment variables that contain passwords or other sensitive information. After you run the scripts, you should double-check that no sensitive information has been left behind in the environment variables.

Securing Source Code, Scripts, and Runtime Files

The files required to build a SQL-NS instance (the ICF, ADFs, source code for custom components, and build scripts) need to be carefully secured. As described in the previous section, they may contain sensitive information such as usernames and passwords, but they may also provide information about the internal structure of the instance and its components that a hacker might be able to exploit.

After the instance is created, the source files and build scripts don't need to reside on the deployment machines. The running instance executes solely against the information in the instance and application database objects, so the ICF and ADFs are not needed. Only files such as XSL transforms for the XsltFormatter and event schema files for the FileSystemWatcher are needed at runtime and do need to be on the deployment machines. These files should be locked down using Windows filesystem security and should be made accessible only to the accounts under which the components that need them run.

Note

Although they aren't needed on the deployment machine, the source files should always be kept in a safe, secure location on another machine. You will need these files to make changes to the application after it is created.


If your SQL-NS applications use custom components, the assemblies containing the implementations of those components need to be on the deployment machines as well. It's especially important to secure these assemblies. Because the SQL-NS engine loads the assemblies at runtime and executes the code they contain, if a malicious user is able to replace your assemblies with ones of his own, his code will run in the SQL-NS engine, under the credentials of the engine's account. Such code could be used to bring down your system, open it up for unauthorized access, or cause the SQL-NS engine to act on the malicious user's behalf.

You can secure custom component assemblies by signing them with a private key and referring to them in the ICF and ADFs with fully qualified strong names. In all this book's examples, the <AssemblyName> elements specified just a path to a DLL. However, every <AssemblyName> element in the ICF and ADF schemas also supports fully qualified strong names. A strong name qualifies the assembly name with an assembly version, culture, and public key token. For example, had our content formatter assembly been signed, it could have been specified in the <AssemblyName> element of a content formatter declaration as follows:

[View full width]

<AssemblyName>NewSongNotificationFormatter, Version=1.0.1234.0, Culture=en-US, PublicKeyToken=b77a5c561934e089d</AssemblyName>


Note

The version number and public key token shown in this snippet are only examples. The content formatter assembly actually used in this book's samples is not stamped with this version number nor signed to work with this public key token.


When you identify an assembly in an ICF or ADF with a strong name, the SQL-NS engine will not load it unless the file has been signed with the correct private key. This greatly reduces the chances that a trusted assembly could be replaced with malicious code.

For further security, you should also set the appropriate filesystem ACLs on your assemblies and run the SQL-NS engine under a low-privileged account. This ensures that even if a hacker is able to get the engine to run arbitrary code, that code would have very few permissions on the system.

Controlling Database Access

As you've already seen, access to SQL-NS databases is controlled via roles. SQL-NS defines a set of roles corresponding to the various functions of a running application. You can assign accounts to these roles as needed.

In the sample applications we looked at in previous chapters, all hosted components run on the same machine, so we made the single engine account a member of the NSRunService role. This role encompasses several other SQL-NS roles that each correspond to the individual functions of the components in the SQL-NS engine.

If you break out the components of the SQL-NS engine onto separate machines, as shown in Figure 13.4, you can run the engine under a different account on each machine. You can make the account used on a particular machine a member of only the roles needed for the components hosted on that machine. For example, the account under which the engine runs on the machine hosting event providers needs membership only in the NSEventProvider role. It does not need membership in the wider NSRunService role, which encompasses the permissions for the generator and distributor as well.

In a deployed SQL-NS instance, you can use different accounts for the SMI, the SQL-NS engine on each machine, standalone event providers, and administration and reporting operations. Each of these accounts can be assigned to the specific roles required. For a complete list of the SQL-NS database roles and the functions they correspond to, see the "Notification Services Database Roles" topic in the SQL-NS Books Online.

Using Throttles

For most SQL-NS applications, it's possible to predict the peak volumes of events, subscriptions, and notifications. Any of these volumes drastically exceeding their expected peaks might be an indication of a problem. The application might accidentally be processing more data than necessary due to a bug, or an intruder might somehow have induced the application into overload (perhaps in an attempt to flood the network with traffic). In either case, you probably want to curtail the application while you assess the cause of the deviation from the expected volumes.

SQL-NS provides a throttling mechanism that can automatically stop notification generation when specified limits are exceeded. Three throttle settings are provided:

  • An event throttle that limits the number of events that can be processed per event class in a given quantum

  • A subscription throttle that limits the number of subscriptions of each subscription class that can be processed in each rule firing

  • A notification throttle that limits the number of notifications that can be generated per notification class, as a result of each rule firing

You can specify these throttle values for an application in the ADF, as shown in Listing 13.4.

Listing 13.4. Configuring Throttle Values in the ADF

 <Application>   ...   <ApplicationExecutionSettings>     ...     <EventThrottle>2000</EventThrottle>     <SubscriptionThrottle>5000</SubscriptionThrottle>     <NotificationThrottle>15000</NotificationThrottle>   </ApplicationExecutionSettings> </Application> 

All three throttle values are specified as part of the <ApplicationExecutionSettings> element, in the <EventThrottle>, <SubscriptionThrottle>, and <NotificationThrottle> subelements, respectively. If the number of events, subscriptions, or notifications in a quantum exceeds the corresponding throttle value, the quantum's processing is halted. If this occurs, the generator writes a message to the Application Event Log, indicating that a throttle has been exceeded.

Note

Do not confuse throttles with the quantum limits described in Chapter 12, "Performance Tuning." Throttles limit the processing done in a single quantum, whereas the quantum limits constrain how many old quantums the generator processes when the quantum clock falls behind.


In your own applications, you should set the throttle values appropriately, based on the expected volumes of events, subscriptions, and notifications. After your application begins running, you should monitor the Application Event Log for messages indicating failures due to throttle limits being exceeded. If you see such failures, investigate whether there is a problem with the application, or whether the throttle values are too small. You can adjust the throttles as needed by editing the values in the ADF and then updating the instance.

The default value for all three throttles is 1,000. This value applies if the <EventThrottle>, <SubscriptionThrottle>, or <NotificationThrottle> elements are omitted from the ADF. If you specify a value of zero for any throttle, the throttle is effectively disabled, and no limit is imposed. Running with the throttles disabled is not recommended; even if you expect large volumes of data, you should still set throttle values, although they may need to be very high.

Caution

Because the throttle elements are all optional, it's easy to forget to adjust these defaults appropriately before deployment. In the development environment, applications are usually tested with small loads, so the throttle values aren't really noticed. However, in deployment, the default throttle value of 1,000, which applies if a throttle element is omitted from the ADF, is often exceeded in normal operation. Before you do large-scale testing or move your application into its deployment environment, make sure that you declare the throttle elements and supply appropriate values if you expect to exceed the defaults.





Microsoft SQL Server 2005 Notification Services
Microsoft SQL Server 2005 Notification Services
ISBN: 0672327791
EAN: 2147483647
Year: 2006
Pages: 166
Authors: Shyam Pather

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net