views:

1172

answers:

3

I need to write a web application using SQL Server 2005, asp.net, and ado.net. Much of the user data stored in this application must be encrypted (read HIPAA).

In the past for projects that required encryption, I encrypted/decrypted in the application code. However, this was generally for encrypting passwords or credit card information, so only a handful of columns in a couple tables. For this application, far more columns in several tables need to be encrypted, so I suspect pushing the encryption responsibilities into the data layer will be better performing, especially given SQL Server 2005's native support for several encryption types. (I could be convinced otherwise if anyone has real, empirical evidence.)

I've consulted BOL, and I'm fairly adept at using google. So I don't want links to online articles or MSDN documentation (its likely I've already read it).

One approach I've wrapped my head around so far is to use a symmetric key which is opened using a certificate.

So the one time setup steps are (performed by a DBA in theory):

  1. Create a Master Key
  2. Backup the Master Key to a file, burn to CD and store off site.
  3. Open the Master Key and create a certificate.
  4. Backup the certificate to a file, burn to CD and store off site.
  5. Create the Symmetric key with encryption algorithm of choice using the certificate.

Then anytime a stored procedure (or a human user via Management Studio) needs to access encrypted data you have to first open the symmetric key, execute any tsql statements or batches, and then close the symmetric key.

Then as far as the asp.net application is concerned, and in my case the application code's data access layer, the data encryption is entirely transparent.

So my questions are:

  1. Do I want to open, execute tsql statements/batches, and then close the symmetric key all within the sproc? The danger I see is, what if something goes wrong with the tsql execution, and code sproc execution never reaches the statement that closes the key. I assume this means the key will remain open until sql kills the SPID that sproc executed on.

  2. Should I instead consider making three database calls for any given procedure I need to execute (only when encryption is necessary)? One database call to open the key, a second call to execute the sproc, and a third call to close the key. (Each call wrapped in its own try catch loop in order to maximize the odds that an open key ultimately is closed.)

  3. Any considerations should I need to use client side transactions (meaning my code is the client, and initiates a transaction, executes several sprocs, and then commits the transaction assuming success)?

A: 
  1. you can use @@error to see if any errors occured during the call to a sproc in SQL.

  2. No to complicated.

  3. You can but I prefer to use transactions in SQL Server itself.

Matthew M. Osborn
About #2, I have a base "data access" class, that makes opening and closing the encryption key pretty much transparent. So I'm not worried too much about complexity, more about the additional database traffic, with each interaction with the database now requiring executing at least three commands.
Jon
+3  A: 

1) Look into using TRY..CATCH in SQL 2005. Unfortunately there is no FINALLY, so you'll have to handle both the success and error cases individually.

2) Not necessary if (1) handles the cleanup.

3) There isn't really a difference between client and server transactions with SQL Server. Connection.BeginTransaction() more or less executes "BEGIN TRANSACTION" on the server (and System.Transactions/TransactionScope does the same, until it's promoted to a distributed transaction). As for concerns with open/closing the key multiple times inside a transaction, I don't know of any issues to be aware of.

Brannon
I don't plan to implement the stored procedures using the CLR capabilities of sql 2005, rather plain vanilla tsql. Or are you saying that tsql now supports try...catch? If so, great!
Jon
Yes SQL 2005 adds support to T-SQL for "exception" handling, but I didn't link to MSDN :) Search for BEGIN TRY.
Brannon
Very cool. Thanks Brannon.
Jon
+1  A: 

I'm a big fan of option 3.

Pretend for a minute you were going to set up transaction infrastructure anyways where:

  1. Whenever a call to the datastore was about to be made if an existing transaction hadn't been started then one was created.
  2. If a transaction is already in place then calls to the data store hook into that transaction. This is often useful for business rules that are raised by save/going-to-the-database events. IE. If you had a rule that whenever you sold a widget you needed to update a WidgetAudit table, you'd probably want to wrap the widget audit insert call in the same transaction as that which is telling the datastore a widget has been sold.
  3. Whenever a the original caller to the datastore (from step 1) is finished it commits/rollbacks the transaction, which affects all the database actions which happened during its call (using a try/catch/finally).

Once this type of transactioning is created then it becomes simple to tack on a open key at the beginning (when the transaction opens) and close the key at the end (just before the transaction ends). Making "calls" to the datastore isn't nearly as expensive as opening a connection to the database. It's really things like SQLConnection.Open() that burns resources (even if ADO.NET is pooling them for you).

If you want an example of these types of codes I would consider looking at NetTiers. It has quite an elegant solution for the transactioning that we just described (assuming you don't already have something in mind).

Just 2 cents. Good luck.

Tyler
Thanks Tyler. I appreciate the feedback. However, I have to think that executing a single command (sproc with open key, various sql statements, and closing the key) will perform better than executing three commands.
Jon
Additionally, regarding transactions, my preference is generally to isolate those to the server side. My tendency is to believe that will scale better, although I cannot prove that claim.
Jon