Sunday, October 31, 2010

Encrypting SSIS Package Configurations

Package configurations provide a means of making packages portable as well as easing, or even automating, the process of moving a package from development, to testing, and finally to production.  This ability is provided by storing connection string information, as well as a wide variety of other package properties, in one or more of the five providers:

  • XML configuration file
  • Environmental variable
  • Registry entry
  • Parent package variable
  • SQL Server table
  • Once the package configurations is completed then the configuration property can be changed to reflect the new value and is loaded at run time, rather than having to re-open the package in BIDS.

    MSDN provides documentation of all five types here as well as outlining how configurations are loaded.  Rafael Salas has posted an excellent technical article as well that outlines the use and creation of package configurations here

    During a recent integrations services class the topic of encrypting connection strings within a package configuration arose. The business need was outlined as requiring that all connection strings be stored in a configuration provider and that the entire string must also be encrypted.  I promised to post the steps necessary to meet this constraint and while typing out the steps I recalled that I discovered this process reading a post from Michael Coles blog.  Rather than re-create what has already been done I prefer to give credit where credit is due.  Michael’s post can be found here and gives step by step instructions and explanation.

    Wednesday, October 27, 2010

    Package Storage: MSDB, Folder, or Package Store?

    SQL Safety has moved!! 

    http://www.sqlsafety.com

    SSIS packages can be stored in three different places, the msdb database, the “package store”, or the file/folder system.  The confusion between these options is typically struggling with the difference between the “package store” and the folder.

    Storing packages in the msdb database is rather straight forward as the packages, and meta-data, is stored in the system table(s) of the database.  When storing packages in the file/folder system and/or the “package store” the lines begin to fade.  The package store, by default, is located at C:\Program Files\Microsoft SQL Server\100\DTS\Packages for both SQL 08 and 08R2.  The truth is that this location, is by definition, a part of the file/folder system, but it differs in the fact that SSIS is configured to monitor this location for packages.  SSIS is made aware of this location through the MsDtsSrvr.ini.xml configuration file which is located, in a default installation, at C:\Program Files\Microsoft SQL Server\100\DTS\Binn.  Examination of the file reveals two Folder elements, one for SqlServerFolder and the other is a FileSystemFolder.  The SqlServerFolder points to the msdb database, while the file system elements is named “File System and has a StorePath of“..\Packages”. 

    In order to demonstrate the difference from a folder and the package store you can deploy a package to a folder system, other then C:\Program Files\Microsoft SQL Server\100\DTS\Packages.  After deploying the package connect to SSIS with SQL server management studio and view Stored Packages>File System and you can see that there are no packages that are showing installed.

    image

    Now use an xml editor or notepad to open the  MsDtsSrvr.ini.xml and change the “..\Packages” value to “C:\Packages”, this folder must be present before making the change.  Save the file and restart the SSIS service, this must be done as the new values will be loaded into the registry upon restart.  From SSMS reconnect to the instance of integration services and refresh the Stored Packages>File System.

    <Folder xsi:type="FileSystemFolder">
      <Name>File System</Name>
      <StorePath>C:\Packages</StorePath>
    </Folder>

    image

    Modifying the config file also provides the ability to add multiple folders within the package store.  Additional elements can be added like the example below which will allow you to monitor and manage these locations through SSMS.  The below is the entire config file:

    <?xml version="1.0" encoding="utf-8"?>
    <DtsServiceConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <StopExecutingPackagesOnShutdown>true</StopExecutingPackagesOnShutdown>
      <TopLevelFolders>
        <Folder xsi:type="SqlServerFolder">
          <Name>MSDB</Name>
          <ServerName>.</ServerName>
        </Folder>
        <Folder xsi:type="FileSystemFolder">
         
    <Name>E Drive</Name>
         
    <StorePath>E:\Packages</StorePath>
       
    </Folder>
       
    <Folder xsi:type="FileSystemFolder">
         
    <Name>File System</Name>
         
    <StorePath>..\Packages</StorePath>
        </Folder>
      </TopLevelFolders>
    </DtsServiceConfiguration>

    image

    Harsh Shah has an excellent post on his blog here that provides excellent information on adding a root level folder.

    The modification of the config file in relation to clustering is outlined here on Microsoft support.

    Modifying the config file to connect to a named instance of SSIS is outlined on my blog here:

    http://sqlsafety.blogspot.com/2009/11/connecting-to-named-instance-of-ssis.html

    Thursday, October 14, 2010

    Transparent Data Encryption

    My last post covered encrypting column level data using t-sql.  The need to encrypt data beyond column or row level may mandated by organizational governance or oversight.  Transparent data encryption is a feature available in SQL Server 2008 Enterprise edition which allows you to encrypt the database files, both log and data, as well as all backups and database snapshots.  As this works at the page level TDE does not increase the size of the database, but is CPU intensive, please refer to the technical article relating to data encryption in SQL Server.

    The steps to enabling TDE begins with the creation of the of a master key in the master database and then creating a certificate from the master key.  It is critical that the certificate be backed up as the database will not be able to be decrypted if it is lost:

    USE MASTER;
    GO
    --Create the master key using a password
    CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'Pa$$w0rd';
    GO
    --Create the certificate with the subject TDE
    CREATE CERTIFICATE TDECert
    WITH SUBJECT='TDE';
    GO
    --ALWAYS backup the certificate to file to assure you will be able to recover or to restore
    BACKUP CERTIFICATE TDECert TO FILE = 'c:\cert\TDECert'
    WITH PRIVATE KEY ( FILE = 'c:\cert\TDECertKey' ,
       
    ENCRYPTION BY PASSWORD = 'Pa$$w0rd' );
    GO  

    Once the certificate is create then you need to create an encryption key in the database you wish to encrypt using the certificate:

    --Change the database to AdventureWorks2008
    USE AdventureWorks2008;
    GO
    --Create the database encryption key using the certificate created in the master database
    CREATE DATABASE ENCRYPTION KEY
    WITH
    ALGORITHM = AES_128
    ENCRYPTION BY SERVER CERTIFICATE TDECert;
    GO

    The only thing left to do is to ALTER the database to enable TDE.  KEEP IN MIND once the database is altered to begin using TDE then all files will be encrypted.  This results in increased disk I/O and CPU to perform the encryption and write the files back to disk.  If this is a production environment then scheduled down time or off hours is recommended to implement the initial encryption:

    ALTER DATABASE AdventureWorks2008
    SET ENCRYPTION ON;
    GO

    Both log and data files associated with the database are now encrypted at the disk level.  This encryption is for both the files and backup files, which means that this will prevent the use of backup compression also available to SQL 2008 Enterprise edition.  The attempt to back up a database using native encryption results in the following error message:

    BACKUP DATABASE AdventureWorks2008
    TO  DISK = N'C:\Backup\ADW.bak'
    WITH COMPRESSION;
    GO

    Msg 3201, Level 16, State 1, Line 1
    Cannot open backup device ‘C:\Backup\ADW.bak’. Operating system error 3(The system cannot find the path specified.).
    Msg 3013, Level 16, State 1, Line 1
    BACKUP DATABASE is terminating abnormally.

    However page and row level compression is still available as can be seen by altering the Person.Address table with page level  data_compression:

    ALTER TABLE Person.Address REBUILD PARTITION = ALL
    WITH (DATA_COMPRESSION = PAGE);

    Another limitation is that TDE operations are not permitted on a database with read-only file groups.  This can be overcome by changing the file group to read/write, enabling TDE, and once the encryption scan is done the file group(s) can be set back to read-only.

    The below query will alter the AdventureWorks2008 database to remove TDE and remove the objects created in the above queries:
    --Alter the database to remove TDE
    ALTER DATABASE AdventureWorks2008
    SET ENCRYPTION OFF;
    GO
    --Drop the database encryption key
    DROP DATABASE ENCRYPTION KEY;
    GO
    --Change the database to master to drop the certificate and key
    USE MASTER;
    GO
    --Drop the certificate
    DROP  CERTIFICATE TDECert;
    GO
    --Drop the master key
    DROP MASTER KEY;
    GO

    Saturday, October 9, 2010

    Encrypting Column Level Data in SQL Server

    The scripts for this post can be downloaded here!!
    http://www.sqlsafety.com/?p=15
    The ability to encrypt data natively using t-sql was provided in SQL Server 2005 with the introduction of SQL Server cryptographic services.  The magic behind this feature originates in the operating system with the data protection api, DPAPI.  The first time an instance of SQL Server is started the "service master key”, SMK, is created.  The SMK is a 128-bit 3DES key which is encrypted using the DPAPI and the credentials of the SQL Server service account.  Once created the SMK is used to encrypt all “database master keys”, DMK’s, and various server side resources, credentials, linked server logins, etc.  The ability to backup, restore, and regenerate the SMK is available through t-sql:
    BACKUP SERVICE MASTER KEY
        TO FILE =
    'C:\SMK\service_master_key'
       
    ENCRYPTION BY PASSWORD = 'Pa$$w0rd'; GO
    RESTORE SERVICE MASTER KEY
        FROM FILE =
    'C:\SMK\service_master_key'
       
    DECRYPTION BY PASSWORD = 'Pa$$w0rd'; GO
    ALTER SERVICE MASTER KEY
        WITH
    NEW_ACCOUNT = 'AdvWorks\sqlserver',
       
    NEW_PASSWORD = 'P@ssw0rd'; GO

    One of the more interesting functions above is the ALTER statement which regenerates the SMK optionally using a new service account.  As the SMK is encrypted on first start up using the credentials of the service account the question arises, what if the service account is changed?  Stuart Padley provides an excellent post that outlines the loading and decryption process of the SMK in SQL Server 2008 and Laurentiu Cristofor outlines considerations to take when changing service accounts.
    In order to encrypt column level data then SMK sits at the root, but a database master key, DMK, is required in each database where data will be encrypted.  The creation and maintenance of the SMK is outside of our control, for the most part, but the creation and maintenance of DMK’s falls directly on the developer/dba.  The first step is to create the DMK:
    -- Use the AdventureWorks database USE AdventureWorks; -- Create a Database Master Key CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'P@ssw0rd'; GO
    Once the key is created it is always considered best practice to back the key up and store it some place safe in case it needs to be recovered at a later time:
    BACKUP MASTER KEY
        TO FILE =
    'c:\ADWDMK\ADWKey'
       
    ENCRYPTION BY PASSWORD = 'P@ssw0rd'; GO

    There is only one DMK per database, it is not created by default, is used solely for data encryption, and is encrypted and decrypted using the SMK.   The DMK does not directly encrypt data, but provides the ability to create keys that are used for data encryption.  The easiest way to demonstrate encrypting data is to create a key that is encrypted with a password:
    CREATE SYMMETRIC KEY TestSymKey WITH ALGORITHM = TRIPLE_DES ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
    OPEN SYMMETRIC KEY TestSymKey
         DECRYPTION
    BY PASSWORD = 'Pa$$w0rd';
    DECLARE @Encryptvalsym VARBINARY(MAX) SET @Encryptvalsym = ENCRYPTBYKEY(KEY_GUID('TestSymKey'),'I TOLD YOU THIS WOULD WORK!!!')
    SELECT CONVERT(VARCHAR(MAX),DecryptByKey(@Encryptvalsym)),
         
    @Encryptvalsym; GO
    CLOSE SYMMETRIC KEY TestSymKey; GO

    The results look something like this:
    Col1: I TOLD YOU THIS WOULD WORK!!!
    Col2: 0x003F3493D8D3604B994544AA3855E5BE0100000007927EE71039F01B3E4F5946E9583D498301F804A12DD55555EC4DE6FF027B53815E9121432DA316832BF2EA5581452D
    The above example demonstrates how to create a symmetric key using the 3DES algorithm* and is encrypted by password.  This is simplistic, but has some glaring shortcomings.  The first item of concern is the requirement of passing the key’s password in clear text for every ENCRYPT or DECRYPT statement.  To mitigate this risk a key can be created using a certificate rather than a password:
    *As I mentioned previously the “out of the box” algorithm’s available are dependant on the operating system unless utilizing enterprise edition and EKM.
    -- Create a Test Certificate CREATE CERTIFICATE TestCertificate
      
    WITH SUBJECT = 'Adventureworks Test Certificate',
      
    EXPIRY_DATE = '10/31/2012'; GO

    --Backup the certificate with the private key BACKUP CERTIFICATE TestCertificate TO FILE = 'c:\cert\TestSymmetricKey' WITH PRIVATE KEY ( FILE = 'c:\cert\TestSymmetricKeykey' ,
       
    ENCRYPTION BY PASSWORD = 'Pa$$w0rd' ); GO

    Once a certificate is created than that certificate can be used for data encryption rather than a password.  The below example demonstrates creating a symmetric key using the newly created TestCertificate, creating a temporary table and populating it with encrypted data from the Person.Contact table:
    -- Create a Symmetric Key CREATE SYMMETRIC KEY TestSymmetricKey
      
    WITH ALGORITHM = TRIPLE_DES
       
    ENCRYPTION BY CERTIFICATE TestCertificate;
       
    GO
        
    --Create a temporary table to hold encrypted data
    -- Create a Temp Table
    CREATE TABLE Person.#Temp (ContactID   INT PRIMARY KEY, FirstName   NVARCHAR(200), MiddleName  NVARCHAR(200), LastName    NVARCHAR(200), eFirstName  VARBINARY(200), eMiddleName VARBINARY(200), eLastName   VARBINARY(200)); GO
    -- EncryptByKey demonstration encrypts 100 names from the Person.Contact table OPEN SYMMETRIC KEY TestSymmetricKey
       DECRYPTION
    BY CERTIFICATE TestCertificate;
      
    GO
    INSERT
    INTO
    Person.#Temp (ContactID, eFirstName, eMiddleName, eLastName) SELECT ContactID,
      
    EncryptByKey(Key_GUID('TestSymmetricKey'), FirstName),
      
    EncryptByKey(Key_GUID('TestSymmetricKey'), MiddleName),
      
    EncryptByKey(Key_GUID('TestSymmetricKey'), LastName) FROM Person.Contact WHERE ContactID <= 100;
    --View the Encrypted data SELECT * FROM Person.#Temp

    The results are displayed here:image
    The data can then be decrypted using the symmetric key and certificate:
    -- DecryptByKey demonstration decrypts the previously encrypted data UPDATE Person.#Temp SET FirstName = DecryptByKey(eFirstName),
      
    MiddleName = DecryptByKey(eMiddleName),
      
    LastName = DecryptByKey(eLastName); GO
    -- View the results SELECT * FROM Person.#Temp; GO

    The FirstName, MiddleName, and LastName columns now contain the decrypted values of hte eFirstName, eMiddleName, and eLastName.  The below query will clean up some of the items created in the previous examples.
    -- Clean up work:  drop temp table, symmetric key, test certificate and master key DROP TABLE Person.#Temp; GO CLOSE SYMMETRIC KEY TestSymmetricKey; GO DROP SYMMETRIC KEY TestSymmetricKey; GO DROP CERTIFICATE TestCertificate; GO DROP SYMMETRIC KEY  TestSymKey; GO
    The ability to encrypt and decrypt data using t-sql is an awesome tool, but this feature tends to benefit the developer a bit more than the database administrator.  Being tasked with maintaining and managing databases that are used for a front end application limits where the dba can implement encryption without having to modify source code or stored procedures created by the developer.
    To give an example of how encryption can be utilized to secure column level data in a more realistic demonstration look at the code below.  The t-sql code will create two logins, login1 and login2, who will be given insert and select permissions on the AdventureWorks database.  A table called employeessn is created that holds all employees names and social security numbers.  A symmetric key is first create with encryption by password and authorization is granted to login1.  Data is inserted into the employeessn table using the symmetric key to encrypt the ssn column.  The statements following the insert statement demonstrate how login1 is able to open the key and decrypt the data while login2 is unable to open the key.  In order to demonstrate how keys can be modified a certificate is created with authorization given to login1 and the symmetric key is altered to use the new certificate and then the encryption by password is dropped.  Using the WITH EXECUTE AS the decrypted data is selected by login1.  Finally a stored procedure is created that will execute as login1 and will utilize the certificate secured key to decrypt the data.  Initially login1 and login2 are granted execute permissions on the procedure and both are able to execute and see the decrypted social security numbers.  To demonstrate the versatility of the WITH EXECUTE AS in the stored procedure login2 is revoked execute permissions of the stored procedure so all attempts to execute result in failure.
      USE MASTER; GO --Create logins login1 and login2 CREATE LOGIN Login1 WITH PASSWORD='Pa$$w0rd'; GO CREATE LOGIN Login2 WITH PASSWORD = 'Pa$$w0rd'; GO --Create database users in AdventureWorks login1 and login2 that map back to the sql server logins USE AdventureWorks; GO CREATE USER login1 FOR LOGIN Login1; GO CREATE USER login2 FOR LOGIN Login2; GO    --Create a table to hold an employees social security # CREATE TABLE employeessn (employee  VARCHAR(50), ssn        VARBINARY(100)) GO    --Give access to this table to login1 and login2 so they can SELECT and INSERT data GRANT SELECT,
        
    INSERT
    TO
    login1; GO GRANT SELECT,
        
    INSERT
    TO
    login2; GO     --Create a symmetric key exncrypted with a password and authorize the user ONLY login1 CREATE SYMMETRIC KEY sensitive_data AUTHORIZATION login1 WITH ALGORITHM=TRIPLE_DES ENCRYPTION BY PASSWORD='Pa$$w0rd'; GO    --Use EXECUTE AS to change the security context of the current query EXECUTE AS LOGIN='login1'; GO    --Open the key with the password OPEN SYMMETRIC KEY sensitive_data
    DECRYPTION
    BY PASSWORD='Pa$$w0rd'; GO     --INSERT data into the table encrypting the ssn INSERT INTO employeessn VALUES ('David', ENCRYPTBYKEY(KEY_GUID('sensitive_data'), '123-45-7777')),
          (
    'Brandon', ENCRYPTBYKEY(KEY_GUID('sensitive_data'), '123-45-6666')),
          (
    'Chase', ENCRYPTBYKEY(KEY_GUID('sensitive_data'), '123-45-8888')),
          (
    'Derek', ENCRYPTBYKEY(KEY_GUID('sensitive_data'), '123-45-9999')); GO CLOSE ALL SYMMETRIC KEYS; GO --View the encrypted ssn's SELECT * FROM employeessn; GO --Open the symmetric key to decrypt the employees ssn OPEN SYMMETRIC KEY sensitive_data
    DECRYPTION
    BY PASSWORD='Pa$$w0rd' GO    SELECT Employee,
         
    CONVERT(VARCHAR, DECRYPTBYKEY(ssn))   FROM employeessn
    GO
    --Close the symmetric key CLOSE ALL SYMMETRIC KEYS
    GO
    --Revert back to sysadmin REVERT; GO --Use EXECUTE AS to change the security context of the current query EXECUTE AS LOGIN='login2'; GO --Attempt to open the symmetric key as login2 and notice that login2 does not have permission to open OPEN SYMMETRIC KEY sensitive_data
    DECRYPTION
    BY PASSWORD='Pa$$w0rd' GO      --Revert back to sysadmin in order to alter the symmetric key and create new certificate REVERT; GO --Now create a new certificate to use for the symmetric key and remove the encryption by password CREATE CERTIFICATE SSNCert AUTHORIZATION login1 WITH SUBJECT='Certificate used to decrypt SSNs'; GO --The key must be open to make modifications OPEN SYMMETRIC KEY sensitive_data
    DECRYPTION
    BY PASSWORD='Pa$$w0rd'; GO --Add the encryption by certificate first ALTER SYMMETRIC KEY sensitive_data ADD ENCRYPTION BY CERTIFICATE SSNCert
    GO
    --Remove the password encryption ALTER SYMMETRIC KEY sensitive_data DROP ENCRYPTION BY PASSWORD= 'Pa$$w0rd';   GO --Close the symmetic key CLOSE ALL SYMMETRIC KEYS; GO --Use EXECUTE AS to change the security context of the current query EXECUTE AS LOGIN='login1'; GO --Open the symmetric key using the certificate OPEN SYMMETRIC KEY sensitive_data
    DECRYPTION
    BY CERTIFICATE SSNCert; GO --SELECT from the employeessn table and decrypt the ssn SELECT employee,
         
    CONVERT(VARCHAR, DECRYPTBYKEY(ssn))   FROM employeessn; GO --Revert back to the sysadmin REVERT; GO --Create a stored procedure that will execute as logn1 and decrypt the employee's ssn CREATE PROC decryptaslogin1
     
    WITH EXECUTE AS 'login1' AS
    OPEN
    SYMMETRIC KEY sensitive_data
    DECRYPTION
    BY CERTIFICATE SSNCert;
    SELECT Employee,
         
    CONVERT(VARCHAR, DECRYPTBYKEY(ssn))   FROM employeessn;
    CLOSE ALL SYMMETRIC KEYS; GO
    --Grant execute permission to both login1 and login2 GRANT EXECUTE ON decryptaslogin1 TO login1; GO GRANT EXECUTE ON decryptaslogin1 TO login2; GO --Use EXECUTE AS to changge the security context of the current query EXECUTE AS LOGIN='login1'; GO --Notice that login1 is able to execute the proc and see the decrypted ssn's EXEC decryptaslogin1; GO --Revert back to sysadmin REVERT; GO --Use EXECUTE AS to change the security context of the current query EXECUTE AS LOGIN='login2'; GO --Notice login2 is able to execute the procedure and see the decrypted ssn's as the procedure is running as login1 EXEC decryptaslogin1; GO --Revert back to sysadmin REVERT; GO --Revoke permissions of execute to login2 for the decryptaslogin1 procedure REVOKE EXECUTE ON decryptaslogin1 TO login2; GO --Use EXECUTE AS to change the security context of the current query EXECUTE AS LOGIN='login2'; GO --Notice that as execute permissions are revoked for login2 execution fails EXEC decryptaslogin1; GO
    REVERT
    ; GO --Assure that the symmetric key is closed before cleanup CLOSE ALL SYMMETRIC KEYS; GO --Cleanup all keys, logins, certs, procs and tables DROP PROC decryptaslogin1; GO DROP TABLE employeessn; GO DROP SYMMETRIC KEY sensitive_data; GO DROP CERTIFICATE SSNCert; GO DROP USER login1; GO DROP USER login2; GO USE MASTER; GO DROP LOGIN login1; GO DROP LOGIN login2; GO

    While preparing this post I came across some excellent reference material which I have tried to include in hyperlinks.  Once such article was written by Michael Coles on SQLServerCentral that provides some great insight and examples on encrypting data using SQL.