How to Encrypt a User-Defined Function in SQL Server

When creating a user-defined function in SQL Server, you have the option of encrypting it.

To create a user-defined function with T-SQL, you use the CREATE FUNCTION syntax. To encrypt it, you add the WITH ENCRYPTION argument.

You can also use the same argument to encrypt an existing function when using ALTER FUNCTION.

When you encrypt a user-defined function in this way, the function’s text is converted to an obfuscated format. The function’s definition is not directly visible in any catalog views. Therefore, the function’s definition cannot be viewed by users that have no access to system tables or database files.

Example 1 – Inline Table-Valued Function with Encryption

Here’s an example of creating an encrypted user-defined table-valued function.

CREATE FUNCTION dbo.udf_CatsByName_ITVF( @CatName varchar(70) )
    RETURNS TABLE
    WITH ENCRYPTION
AS
RETURN (
    SELECT 
        CatId,
        CatName,
        Phone
    FROM dbo.Cats
    WHERE CatName = @CatName
    );

GO

The part for encrypting it is WITH ENCRYPTION. I could simply remove that argument if I didn’t want to encrypt it.

After creating that function, now when I use the sys.sql_modules system catalog view to view its definition, I get NULL.

SELECT definition 
FROM sys.sql_modules
WHERE object_id = OBJECT_ID('udf_CatsByName_ITVF');

Result:

+--------------+
| definition   |
|--------------|
| NULL         |
+--------------+

And here’s the error message I get in Azure Data Studio when I try to script the function:

No script was returned when scripting as Create on object UserDefinedFunction

And I would get a similar message if I tried to view it in SSMS, DBeaver, or any other GUI database management software.

Example 2 – Multi-Statement Table-Valued Function with Encryption

Here’s a multi-statement TVF that does the same as the previous function. Multi-Statement TVFs have a different syntax to inline TVFs. On Multi-Statement TVFs, you put the encryption option after you specify the return variable.

CREATE FUNCTION [dbo].[udf_CatsByName_MSTVF]( @CatName varchar(70) )
    RETURNS @cats TABLE (
        CatId int,
        CatName varchar(70),
        Phone varchar(10)
    )
    WITH ENCRYPTION
AS
BEGIN
    INSERT INTO @cats
    SELECT 
        CatId,
        CatName,
        Phone
    FROM dbo.Cats
    WHERE CatName = @CatName;

    RETURN;
END;

GO

Example 3 – Scalar Function with Encryption

And here’s an example of an encrypted scalar function:

CREATE FUNCTION dbo.discountPrice( 
    @price DECIMAL(12,2), 
    @discount DECIMAL(12,2) 
    ) 
RETURNS DECIMAL (12,2) 
WITH ENCRYPTION
AS
BEGIN
  RETURN @price * (1 - @discount);
END;
GO

Example 4 – Add Encryption to an Existing Function

If you want to encrypt an existing function, use ALTER FUNCTION with the same definition. In other words, I can take the first example, and replace CREATE with ALTER.

ALTER FUNCTION dbo.udf_CatsByName_ITVF( @CatName varchar(70) )
    RETURNS TABLE
    WITH ENCRYPTION
AS
RETURN (
    SELECT 
        CatId,
        CatName,
        Phone
    FROM dbo.Cats
    WHERE CatName = @CatName
    );

GO

This obviously assumes that the rest of the function’s definition is exactly the same as the existing function.

The easiest way to ensure that you’re using the same definition is to use your GUI tool to script the existing function using the “Script as Alter” option, if it exists. Otherwise you could use “Script as Create”, then when the definition appears, change CREATE with ALTER.

If you only have a command line interface, you could query the sys.sql_modules view to get the existing definition (like in the previous example). You can then copy the definition and replace CREATE with ALTER.

Once you’ve done that, you can add WITH ENCRYPTION and run it again.

Example 5 – Adding Multiple Arguments

You can specify multiple arguments as a comma-separated list. For example, if you want to use encryption and you want to specify schema binding, then you’d need to add these as a comma separated list.

CREATE FUNCTION dbo.udf_CatsByName_ITVF( @CatName varchar(70) )
    RETURNS TABLE
    WITH SCHEMABINDING, ENCRYPTION
AS
RETURN (
    SELECT 
        CatId,
        CatName,
        Phone
    FROM dbo.Cats
    WHERE CatName = @CatName
    );

GO

In other words, you only specify WITH once – no need to repeat it for each argument.

Important Notes

Here are some things you should know about encrypting user-defined functions in SQL Server:

  • Privileged users that can access system tables over the DAC port or directly access database files will still be able to view the function’s (non-encrypted) definition.
  • Users that can attach a debugger to the server process can retrieve the original procedure from memory at runtime.
  • Using encryption prevents the function from being published as part of SQL Server replication.
  • CLR functions cannot be encrypted.