是否有可能在通过SQLCMD脚本创建SQL Server 2008数据库之后加密所有现有存储过程?
我想这样做的原因如下:
我想开发没有加密的存储过程,所以我可以轻松点击SQL Server Management Studio中的“修改”来检查它们的内容。
但是,对于部署我想加密它们,所以我想也许我可以写一个脚本,只有在它们被创建后加密它们。对于开发系统,我不会在最终用户系统上运行脚本,而是运行脚本。
答案 0 :(得分:4)
您可能需要查看Encrypting all the Stored Procedures of a Database:
如果您决定需要保护您的SQL存储 程序和思想加密是一个好主意,非常小心! 加密数据库存储过程不应该没有 拥有备份文件或存储的某种源代码管理 程序。我说这是因为,一旦加密, 没有转身。 (是的,有第三方工具 将解密你的代码,但为什么要经历这个麻烦。)
这个技巧是我开发的,因为我的公司需要在不同的服务器上托管应用程序,我们担心 关于我们的代码遭到入侵。所以,为了交付数据库,我们 决定加密所有存储过程。超过一百 写的程序,我不想打开每个程序并粘贴 每个存储过程中都有“加密”。 (对于那些人 你不知道如何加密,请参阅如何保护我的存储 程序代码[^])。所以我决定制作自己的小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;
}
}
}
答案 1 :(得分:3)
我有同样的问题。
我的解决方案是在所有存储过程中加上“ - WITH ENCRYPTION”。该版本由开发人员使用并存储在源代码管理中。
然后我在我的版本中使用一个工具(比如sed)在我发送它们之前用文件中的“WITH ENCRYPTION”替换“WITH ENCRYPTION”。
对于纯SQL解决方案,您可以使用REPLACE。
答案 2 :(得分:2)
WITH ENCRYPTION
表示proc后面的代码不存储在SysComments表中。
您可以编写一个执行exec sp_helptext 'MyProcName'
的脚本并将内容放入 VarChar(MAX)中,以便它可以轻松地保存多行/大型程序,然后从原始程序修改该程序状态
CREATE MyProcName AS
SELECT SecretColumns From TopSecretTable
将CREATE
更改为ALTER
和AS
,将空格或制表符或换行符(使用正则表达式的好地方)包围到WITH ENCRYPTION AS
ALTER MyProcName WITH ENCRYPTION AS
SELECT SecretColumns From TopSecretTable
这将隐藏生产服务器上存储过程的所有代码。
对于要加密的特定类型和/或命名约定的所有对象,您可以将其放在LOOP
或CURSOR
(实际上不是基于集合的操作IMHO)中,并运行它每次你部署。
答案 3 :(得分:1)
我建议在多行字符串变量中创建sproc,然后使用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-mode命令行变量来选择各种行为,这使得它可重复且易于测试。
答案 4 :(得分:0)
使用此查询加密数据库中的所有过程
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
答案 5 :(得分:0)
我写了一个光标,逐步完成并加密大多数对象。
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
答案 6 :(得分:0)
1)我导出为SP和功能创建代码。保持备份。例如D:\ SP2.sql“
2)此事务处理SQL代码,生成用于删除现有sP和函数的脚本
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
答案 7 :(得分:0)
我已通过消除对初始Begin标签的依赖关系对上述答案之一进行了更新。我遇到的情况是,并非所有存储过程都具有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