5

在通过 SQLCMD 脚本创建 SQL Server 2008 数据库的所有现有存储过程之后,是否有可能加密它们?

我想这样做的原因如下:
我想开发没有加密的存储过程,这样我就可以轻松地在 SQL Server Management Studio 中单击“修改”来检查它们的内容。
但是,对于部署,我想对它们进行加密,所以我想也许我可以编写一个脚本,仅在它们创建后加密它们。对于开发系统,我根本不会运行脚本,而在最终用户系统上将运行脚本。

4

8 回答 8

5

您可能需要检查Encrypting all the Stored Procedures of a Database

如果您决定需要保护您的 SQL 存储过程,并且认为加密是个好主意,请务必小心!!!在没有备份文件或存储过程的某种源代码控制的情况下,不应该加密数据库存储过程。我这么说的原因是,一旦它们被加密,就没有回头路了。(是的,有第三方工具可以解密你的代码,但是为什么要经历那个麻烦。)

这个技巧是我开发的,因为我的公司需要将应用程序托管在不同的服务器上,并且我们担心我们的代码会被泄露。因此,为了交付数据库,我们决定加密所有存储过程。编写了一百多个过程,我不想打开每个过程并在每个存储过程中粘贴“WITH ENCRYPTION”。(不知道如何加密的朋友可以参考 How Do I Protect My Stored Procedure Code[^])。所以我决定制作我自己的小 C# 应用程序来做同样的事情。

此应用程序是使用 Visual Studio 2005 和 SQL server 2005 制作的控制台应用程序。输入参数为数据库名称、服务器地址、数据库用户名和密码。一旦您能够提供这些详细信息,您就可以对所有存储过程进行加密了。

我已将我的应用程序代码按原样放在这里。要使此代码正常工作,您需要向应用程序添加“Microsft.SQlserver.SMO”引用,以便可以访问“Database”和“StoredProcedure”等类。

BEFORE YOU DO THIS, TAKE A BACKUP!!!!!!!
//Connect to the local, default instance of SQL Server. 
string DB = "";
ServerConnection objServerCOnnection = new ServerConnection();
objServerCOnnection.LoginSecure = false;
Console.WriteLine("Enter name or IP Address of the Database Server.");
objServerCOnnection.ServerInstance = Console.ReadLine();
Console.WriteLine("Enter name of the Database");
DB = Console.ReadLine();
Console.WriteLine("Enter user id");
objServerCOnnection.Login = Console.ReadLine();
Console.WriteLine("Enter Password");
objServerCOnnection.Password = Console.ReadLine();
Console.WriteLine(" ");
Server srv = new Server();
try // Check to see if server connection details are ok.
{
   srv = new Server(objServerCOnnection);
   if (srv == null)
   {
      Console.WriteLine("Server details entered are wrong,"
         + " Please restart the application");
      Console.ReadLine();
      System.Environment.Exit(System.Environment.ExitCode);
   }
}
catch
{
   Console.WriteLine("Server details entered are wrong,"
      + " Please restart the application");
   Console.ReadLine();
   System.Environment.Exit(System.Environment.ExitCode);
}
Database db = new Database();
try // Check to see if database exists.
{
   db = srv.Databases[DB];
   if (db == null)
   {
      Console.WriteLine("Database does not exist on the current server,"
         + " Please restart the application");
      Console.ReadLine();
      System.Environment.Exit(System.Environment.ExitCode);
   }
}
catch
{
   Console.WriteLine("Database does not exist on the current server,"
      + " Please restart the application");
   Console.ReadLine();
   System.Environment.Exit(System.Environment.ExitCode);
}
string allSP = "";
                
for (int i = 0; i < db.StoredProcedures.Count; i++)
{
   //Define a StoredProcedure object variable by supplying the parent database 
   //and name arguments in the constructor. 
   StoredProcedure sp;
   sp = new StoredProcedure();
   sp = db.StoredProcedures[i];
   if (!sp.IsSystemObject)// Exclude System stored procedures
   {
      if (!sp.IsEncrypted) // Exclude already encrypted stored procedures
      {
         string text = "";// = sp.TextBody;
         sp.TextMode = false;
         sp.IsEncrypted = true;
         sp.TextMode = true;
         sp.Alter();
 
         Console.WriteLine(sp.Name); // display name of the encrypted SP.
         sp = null;
         text = null;
      }
   }
}
于 2011-12-12T17:11:45.607 回答
4

我也有同样的问题。

我的解决方案是将“-- WITH ENCRYPTION”放在我所有的存储过程中。此版本由开发人员使用并存储在源代码管理中。

然后,我在构建中使用工具(如 sed)将文件上的“-- WITH ENCRYPTION”替换为“WITH ENCRYPTION”,然后再发送文件进行安装。

对于纯 SQL 解决方案,您可以使用 REPLACE。

于 2009-11-25T15:55:23.993 回答
2

WITH ENCRYPTION意味着 proc 后面的代码没有存储在 SysComments 表中。

您可以编写一个脚本来执行 aexec sp_helptext 'MyProcName'并将内容放入VarChar (MAX),以便它可以轻松地保存多行/大型程序,然后从原始状态修改程序

CREATE MyProcName AS

SELECT SecretColumns From TopSecretTable

更改CREATEALTER并被AS空格或制表符或换行符包围(使用正则表达式的好地方)WITH ENCRYPTION AS

ALTER MyProcName WITH ENCRYPTION AS

SELECT SecretColumns From TopSecretTable

这将隐藏生产服务器上存储过程的所有代码。

对于要加密的特定类型和/或命名约定的所有对象,您可以将它放在一个LOOP或一个CURSOR(不是真正基于集合的操作恕我直言)中,并在每次部署时运行它。

于 2009-11-25T15:50:33.893 回答
1

我建议在多行字符串变量中创建存储过程,然后使用sp_executesql. 这种方法唯一令人讨厌的缺点是字符串的单引号加倍。

DECLARE @action varchar(max);
SET @action = 'CREATE'; /* or "ALTER" */

DECLARE @withEncryption varchar(max);
SET @withEncryption = ''; /* or "WITH ENCRYPTION" */

DECLARE @sql varchar(max);
SET @sql = @action + ' PROCEDURE dbo.Something'
    (
        ....
    ) ' + @withEncryption +
    ' AS
    BEGIN
        DECLARE @bob varchar(10);
        SET @bob = ''Bob'';
        ....
    END;
    ';

EXEC sp_executesql @statement = @sql;

[注意变量周围的空格。]

我所有的脚本都使用这种方法,一旦你习惯了引号加倍的东西,它就会很好地工作。

我还使用批处理文件来调用脚本,并使用 SQLCMD 模式命令行变量来选择各种行为,这使其可重复且易于测试。

于 2009-11-25T16:18:56.783 回答
1

使用此查询加密数据库中的所有过程

    CREATE TABLE #backup
(
id BIGINT IDENTITY(1, 1),
sptext NVARCHAR(MAX) NOT NULL,
spname NVARCHAR(100) NOT NULL,
encrypttext NVARCHAR(MAX) NULL,
encryptstatus BIT NOT NULL
DEFAULT ( 0 )
)
DECLARE @sptexttable TABLE
(
id BIGINT IDENTITY(1, 1),
sptext NVARCHAR(MAX),
spname NVARCHAR(100)
)
INSERT INTO @sptexttable ( sptext, spname )
SELECT [text],
[name]
FROM syscomments
JOIN sysobjects ON syscomments.id = sysobjects.id
AND sysobjects.xtype = 'p'
DECLARE @sptext NVARCHAR(MAX)
DECLARE @spname NVARCHAR(100)
DECLARE @counter INT
SET @counter = 1
WHILE @counter <= ( SELECT MAX(id)
FROM @sptexttable
)
BEGIN
BEGIN TRY

INSERT INTO #backup ( sptext, spname )
SELECT sptext,
spname
FROM @sptexttable
WHERE id = @counter
END TRY
BEGIN CATCH
END CATCH

IF NOT EXISTS ( SELECT [name]
FROM sysobjects
WHERE [name] = 'ce_LastIndexOf'
AND xtype = 'FN' ) 
BEGIN


EXEC
( 'CREATE FUNCTION ce_LastIndexOf
(
@strValue VARCHAR(4000),
@strChar VARCHAR(50)
)
RETURNS INT
AS BEGIN
DECLARE @index INT


SET @index = 0


WHILE CHARINDEX(@strChar, @strValue) > 0
BEGIN
SET @index = @index
+ CASE WHEN CHARINDEX(@strChar, @strValue) > 1
THEN ( LEN(@strValue) - LEN(SUBSTRING(@strValue,
CHARINDEX(@strChar, @strValue)
+ LEN(@strChar),
LEN(@strValue))) )
ELSE 1
END
SET @strValue = SUBSTRING(@strValue,
CHARINDEX(@strChar, @strValue)
+ LEN(@strChar), LEN(@strValue))
END
RETURN @index
END'
)


END
DECLARE @tempproc NVARCHAR(MAX)
DECLARE @procindex INT
DECLARE @beginindex INT
DECLARE @header NVARCHAR(MAX)
DECLARE @asindex INT
DECLARE @replacetext NVARCHAR(MAX)

SET @tempproc = ( SELECT sptext
FROM @sptexttable
WHERE id = @counter
)

IF ( SELECT CHARINDEX('CREATE PROC', UPPER(@tempproc))
) > 0 
BEGIN
BEGIN TRY
SELECT @procindex = CHARINDEX('PROC', UPPER(@tempproc))
PRINT @procindex
SELECT @beginindex = CHARINDEX('BEGIN', UPPER(@tempproc))
PRINT @beginindex
SELECT @header = SUBSTRING(@tempproc, @procindex,
@beginindex - @procindex)
SELECT @asindex = ( SELECT dbo.ce_lastindexof(@header, 'AS')
- 2
)
SELECT @replacetext = STUFF(@header, @asindex, 10,
CHAR(13) + 'WITH ENCRYPTION'
+ CHAR(13) + 'AS' + CHAR(13))
SET @tempproc = REPLACE(@tempproc, @header, @replacetext)

END TRY
BEGIN CATCH
END CATCH
END

UPDATE @sptexttable
SET sptext = @tempproc
WHERE id = @counter


--PLAY HERE TO M AKE SURE ALL PROCS ARE ALTERED
UPDATE @sptexttable
SET sptext = ( SELECT REPLACE(sptext, 'CREATE PROC',
'ALTER PROC')
FROM @sptexttable
WHERE id = @counter
)
WHERE id = @counter
SELECT @sptext = sptext,
@spname = spname
FROM @sptexttable
WHERE id = @counter
BEGIN TRY
EXEC ( @sptext
)
UPDATE #backup
SET encrypttext = @sptext,
encryptstatus = 1
WHERE id = @counter
END TRY
BEGIN CATCH
PRINT 'the stored procedure ' + @spname
+ ' cannot be encrypted automatically'
END CATCH
SET @counter = @counter + 1
END
SELECT *
FROM #backup
于 2016-01-13T14:14:33.300 回答
1

我写了一个游标,遍历并加密了大多数对象。

                            DECLARE cur_ENCRYPT_ANTHING CURSOR READ_ONLY
                            FOR
                                    SELECT  STUFF(src.definition,
                                                  CASE WHEN CHARINDEX('AS' + CHAR(13),src.definition,1) = 0
                                                       THEN CASE WHEN CHARINDEX('AS ' + CHAR(13),src.definition,1) = 0 THEN CHARINDEX('AS ',src.definition,1)
                                                                 ELSE CHARINDEX('AS ' + CHAR(13),src.definition,1)
                                                            END
                                                       ELSE CHARINDEX('AS' + CHAR(13),src.definition,1)
                                                  END,3,'WITH ENCRYPTION AS' + CHAR(13))
                                    FROM    (SELECT o.name
                                             ,      STUFF(RIGHT(sm.definition,LEN(sm.definition) - CHARINDEX('CREATE ',sm.definition,1) + 1),1,6,'ALTER') AS definition
                                             FROM   sys.sql_modules AS sm
                                                    JOIN sys.objects AS o ON sm.object_id = o.object_id
                                             WHERE  CAST(CASE WHEN sm.definition IS NULL THEN 1
                                                              ELSE 0
                                                         END AS BIT) = 0
                                                    AND type <> 'TR'
                                            ) AS src








                            DECLARE @VLS NVARCHAR(MAX)
                            OPEN cur_ENCRYPT_ANTHING

                            FETCH NEXT FROM cur_ENCRYPT_ANTHING INTO @VLS
                            WHILE (@@fetch_status <> -1)
                                  BEGIN
                                        IF (@@fetch_status <> -2)
                                           BEGIN
                                                 BEGIN TRY
                                                       EXEC (@VLS)

                                                 END TRY
                                                 BEGIN CATCH
                                                       PRINT ERROR_MESSAGE()
                                                       PRINT ''

                                                       PRINT @VLS
                                                 END CATCH
                                           END
                                        FETCH NEXT FROM cur_ENCRYPT_ANTHING INTO @VLS
                                  END

                            CLOSE cur_ENCRYPT_ANTHING
                            DEALLOCATE cur_ENCRYPT_ANTHING
于 2016-03-18T05:35:24.513 回答
1

我通过删除对初始 Begin Tag 的依赖对上述答案之一进行了更新。我有一种情况,不是所有的存储过程都有 BEGIN 和 END。

我改用了 AS 子句,还使用了区分大小写的 charindex 版本(通过添加排序规则)

它不是一个完美的解决方案,但有助于加密我的更多存储过程。

这是我更新的代码:

            IF OBJECT_ID('tempdb..#backup', 'U') IS NOT NULL 

             BEGIN

             DROP TABLE #backup

             END

            CREATE TABLE #backup

             (

             id BIGINT IDENTITY(1, 1),

             sptext NVARCHAR(MAX) NOT NULL,

             spname NVARCHAR(100) NOT NULL,

             encrypttext NVARCHAR(MAX) NULL,

             encryptstatus BIT NOT NULL

             DEFAULT ( 0 )

             )

            DECLARE @sptexttable TABLE

             (

             id BIGINT IDENTITY(1, 1),

             sptext NVARCHAR(MAX),

             spname NVARCHAR(100)

             )

            INSERT INTO @sptexttable ( sptext, spname )

             SELECT [text],

             [name]

             FROM syscomments

             JOIN sysobjects ON syscomments.id = sysobjects.id

             AND sysobjects.xtype = 'p'

            DECLARE @sptext NVARCHAR(MAX)

            DECLARE @spname NVARCHAR(100)

            DECLARE @counter INT

            SET @counter = 1

            WHILE @counter <= ( SELECT MAX(id)

             FROM @sptexttable

             )

             BEGIN





             BEGIN TRY





             INSERT INTO #backup ( sptext, spname )

             SELECT sptext,

             spname

             FROM @sptexttable

             WHERE id = @counter

             END TRY

             BEGIN CATCH

             END CATCH

              IF NOT EXISTS ( SELECT [name]

             FROM sysobjects

             WHERE [name] = 'CaseSensitiveIndex'

             AND xtype = 'FN' ) 

             BEGIN
                

             EXEC (
             'CREATE FUNCTION dbo.CaseSensitiveIndex(@source nvarchar(max), @pattern VARCHAR(50))
            RETURNS int
            BEGIN  
                return   CHARINDEX(@pattern COLLATE Latin1_General_CS_AS, @source COLLATE Latin1_General_CS_AS) 
            END; '
            )
            end


             IF NOT EXISTS ( SELECT [name]

             FROM sysobjects

             WHERE [name] = 'ce_LastIndexOf'

             AND xtype = 'FN' ) 

             BEGIN

                

             EXEC

             ( 'CREATE FUNCTION ce_LastIndexOf 

                (@strValue VARCHAR(max),

                @strChar VARCHAR(50)) 

            RETURNS INT

            AS

            BEGIN

            DECLARE @index INT

                

            SET @index = 0



            WHILE CHARINDEX(@strChar, @strValue) > 0

                BEGIN

                    SET @index = @index + CASE WHEN CHARINDEX(@strChar, @strValue) > 1 

                                 THEN 

                                    (LEN(@strValue) - LEN(SUBSTRING(@strValue,CHARINDEX(@strChar, @strValue) + LEN(@strChar),LEN(@strValue)))) 

                                 ELSE 

                                    1 

                                 END

                    SET @strValue = SUBSTRING(@strValue,CHARINDEX(@strChar, @strValue) + len(@strChar),LEN(@strValue))    

                END



                RETURN @index 

            END'

             )



             END 

             DECLARE @tempproc NVARCHAR(MAX) 

             DECLARE @procindex INT

             DECLARE @beginindex INT

             DECLARE @header NVARCHAR(MAX)

             DECLARE @asindex INT

             DECLARE @replacetext NVARCHAR(MAX)



             SET @tempproc = ( SELECT sptext

             FROM @sptexttable

             WHERE id = @counter

             )



             IF ( SELECT CHARINDEX('CREATE PROC', UPPER(@tempproc))

             ) > 0 

             BEGIN

             BEGIN TRY

             SELECT @procindex = CHARINDEX('PROC', UPPER(@tempproc))

             PRINT @procindex

             SELECT @beginindex=(select dbo.CaseSensitiveIndex(@tempproc, 'AS'))


             if(@beginindex=0) begin set @beginindex=( SELECT dbo.ce_lastindexof(@tempproc, 'AS'))end
             SELECT @header = SUBSTRING(@tempproc, @procindex,

             @beginindex )

             SELECT @asindex = ( SELECT dbo.ce_lastindexof(@header, 'AS')

             - 2

             )

             SELECT @replacetext = STUFF(@header, @asindex, 3,

             CHAR(13) + 'WITH ENCRYPTION'

             + CHAR(13) + 'AS' + CHAR(13))

             SET @tempproc = REPLACE(@tempproc, @header, @replacetext)



                                



             END TRY

             BEGIN CATCH

             END CATCH



                

             END



             UPDATE @sptexttable

             SET sptext = @tempproc

             WHERE id = @counter 



            --PLAY HERE TO MAKE SURE ALL PROCS ARE ALTERED

             UPDATE @sptexttable

             SET sptext = ( SELECT REPLACE(sptext, 'CREATE PROC',

             'ALTER PROC')

             FROM @sptexttable

             WHERE id = @counter

             )

             WHERE id = @counter 



             SELECT @sptext = sptext,

             @spname = spname

             FROM @sptexttable

             WHERE id = @counter


             BEGIN TRY


             EXEC ( @sptext)

             UPDATE #backup

             SET encrypttext = @sptext,

             encryptstatus = 1

             WHERE id = @counter

             END TRY

             BEGIN CATCH

             PRINT 'the stored procedure ' + @spname

             + ' cannot be encrypted automatically'

             END CATCH





             SET @counter = @counter + 1

             END

            SELECT *

            FROM #backup where encryptstatus =0
于 2018-08-30T22:18:32.830 回答
0

1) 我为 SP 和函数导出创建代码。保持备份。例如 D:\SP2.sql"

2)本次交易SQL代码,生成删除现有sP&Functions的脚本

SELECT 'DROP PROCEDURE  [' + SCHEMA_NAME(p.schema_id) + '].[' + p.NAME + ']'  as A
FROM sys.procedures p
union
SELECT  'DROP FUNCTION ' + [name]  
FROM sysobjects WHERE [type] IN (N'FN', N'IF', N'TF', N'FS', N'FT') AND category = 0
order by a

3) 这个 Poweshell 代码替换

AS
BEGIN

经过

WITH ENCRYPTION 
AS
BEGIN

编码

$File = "D:\SP2.sql"
$File2 = $File.Replace("SP2.sql","SP-WithEncrypt.sql")
$sortie=""
$SP = get-content -path $file
echo $SP.Count
For ($i = 0 ; $i -le $SP.Count)
{ if ($sp[$i] -eq "AS" -and $sp[$i+1] -eq "BEGIN")
   { $AEcrire = "`nWITH ENCRYPTION `n AS `n BEGIN"
   $i+=1 
          }
   else
   {$AEcrire =$sp[$i]
   }
   $sortie += "`n$AEcrire"

 $i+=1 
 $SP.Count-$i
}

$sortie| out-file $File2

使用 .replace(,) 会更快,但行尾问题...

4) 在 SSMS 中运行 SP-WithEncrypt.sql

于 2018-07-16T21:34:56.403 回答