Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encrypt the stored procedure created by SqlDependency

Tags:

c#

sql-server

I created a SqlDependency so that an event would fire when the results of a particular query change.

// Create a command
SqlConnection conn = new SqlConnection(connectionString);
string query = "SELECT MyColumn FROM MyTable;";
SqlCommand cmd = new SqlCommand(query, conn)
cmd.CommandType = CommandType.Text;

// Register a dependency
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += DependencyOnChange;

When this code executes, a stored procedure is automatically created with a name like

SqlQueryNotificationStoredProcedure-82ae1b92-21c5-46ae-a2a1-511c4f849f76

This procedure is unencrypted, which violates requirements I have been given. I have two options:

  1. Convince the customer that it doesn't matter that the auto generated procedure is unencrypted because it only does cleanup work and contains no real information (thanks to ScottChamberlain for pointing this out).
  2. Find a way to encrypt the stored procedure generated by SqlDependency.

How can I accomplish option 2?


Contents of the stored procedure in question:

CREATE PROCEDURE [dbo].[SqlQueryNotificationStoredProcedure-b124707b-23fc-4002-aac3-4d52a71c5d6b]
AS
BEGIN
    BEGIN TRANSACTION;

    RECEIVE TOP (0) conversation_handle
    FROM [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b];

    IF (
        SELECT COUNT(*)
        FROM [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b]
        WHERE message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer'
    ) > 0
    BEGIN
        IF (
            ( 
                SELECT COUNT(*)
                FROM sys.services
                WHERE NAME = 'SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b'
            ) > 0
        )
        DROP SERVICE [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b];

        IF (OBJECT_ID('SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b', 'SQ') IS NOT NULL)
            DROP QUEUE [SqlQueryNotificationService-b124707b-23fc-4002-aac3-4d52a71c5d6b];

        DROP PROCEDURE [SqlQueryNotificationStoredProcedure-b124707b-23fc-4002-aac3-4d52a71c5d6b];
    END

    COMMIT TRANSACTION;
END
GO
like image 840
Rainbolt Avatar asked Dec 03 '14 18:12

Rainbolt


People also ask

How do I encrypt a stored procedure?

To encrypt it, you add the WITH ENCRYPTION argument. You can also use the same argument to encrypt an existing procedure when using ALTER PROCEDURE . When you encrypt a stored procedure in this way, the procedure's text is converted to an obfuscated format. Its definition is not directly visible in any catalog views.

How do I encrypt and decrypt a stored procedure in SQL Server?

SQL Server's built-in way to decrypt stored-procedures requires setting up a Dedicated Administrator Connection (DAC) to the SQL Server instance from SQL Server Management Studio on the server. You then need to extract the encrypted value of the procedure definition from sys.

How do I view encrypted functions in SQL Server?

Drill-down until you reach the target database object in the Object Explorer windows as in below screenshot from the SQL Decryptor tool. Then right click on the stored procedure and choose Show DDL script menu option from the context menu. Or simply double-click on the encrypted stored procedure. That is it!

How do you find where a stored procedure is used?

Using SQL Server Management Studio Expand Databases, expand the database in which the procedure belongs, and then expand Programmability. Expand Stored Procedures, right-click the procedure and then click View Dependencies. View the list of objects that depend on the procedure.


1 Answers

Create a DDL trigger that checks if a procedure with a name like "SqlQueryNotificationStoredProcedure-" is being created, and if so, immediately alter it WITH ENCRYPTION instead:

CREATE TRIGGER [TR_EncryptQueryNotificationProcedures] 
ON DATABASE
AFTER CREATE_PROCEDURE, ALTER_PROCEDURE
AS
BEGIN
    SET ARITHABORT ON;
    SET NOCOUNT ON;
    IF TRIGGER_NESTLEVEL() > 1 RETURN;

    -- For debugging purposes only
    PRINT CONVERT(NVARCHAR(MAX), EVENTDATA());

    DECLARE @DatabaseName NVARCHAR(128);
    SET @DatabaseName = EVENTDATA().value(
        '(/EVENT_INSTANCE/DatabaseName)[1]', 'NVARCHAR(128)'
    );
    DECLARE @Schema NVARCHAR(128);
    SET @Schema = EVENTDATA().value(
        '(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(128)'
    );
    DECLARE @Name NVARCHAR(128);
    SET @Name = EVENTDATA().value(
        '(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(128)'
    );

    DECLARE @Definition NVARCHAR(MAX);
    SELECT @Definition = 
        OBJECT_DEFINITION(
            OBJECT_ID(
                QUOTENAME(@DatabaseName) + '.' + 
                QUOTENAME(@Schema) + '.' + 
                QUOTENAME(@Name),
                'P'
            )
        )
    ;

    -- If the sproc is already encrypted, we can't do anything with it
    IF @Definition IS NULL RETURN;  

    SELECT @Definition = STUFF(
        @Definition, 
        CHARINDEX('CREATE', @Definition), 
        LEN('CREATE'), 
        'ALTER'
    );

    IF 
        @Name LIKE 'SqlQueryNotificationStoredProcedure-%' AND
        -- this should always be false since we can't read encrypted definitions, 
        -- but just to make sure 
        @Definition NOT LIKE '%WITH ENCRYPTION AS BEGIN%' 
    BEGIN;
        SET @Definition = REPLACE(
            @Definition, 'AS' + CHAR(13) + CHAR(10) + 'BEGIN', 
            'WITH ENCRYPTION AS BEGIN'
        );
        EXEC (@Definition);
    END;
END;
GO
ENABLE TRIGGER [TR_EncryptQueryNotificationProcedures] ON DATABASE;

Disclaimer: not tested against an actual dependency notification, but the basic idea is sound. It's quite brittle because it depends on the exact form of the procedure, of course -- making it more robust is possible, but tedious.

like image 170
Jeroen Mostert Avatar answered Sep 28 '22 18:09

Jeroen Mostert