Comment chiffrer toutes les procédures stockées d'une base de données existante
-
22-09-2019 - |
Question
Y at-il possibilité de chiffrer toutes les procédures existantes stockées d'une base de données SQL Server 2008 après qu'ils ont été créés par un script SQLCMD?
La raison pour laquelle je veux faire est la suivante:
Je voudrais développer les procédures stockées sans cryptage afin que je puisse facilement cliquer sur « Modifier » dans SQL Server Management Studio pour vérifier leur contenu.
Cependant, pour le déploiement, je voudrais les chiffrer, donc je pensais que peut-être que je pourrais écrire un script qui les encrypte seulement après leur création. Pour les systèmes de dev je simplement n'exécuter le script alors que sur les systèmes utilisateur final le script sera exécuté.
La solution
J'ai le même problème.
Ma solution est de mettre « - AVEC CRYPTAGE » dans toutes mes procédures stockées. Cette version est utilisée par les développeurs et stockés dans le contrôle source.
Je puis utiliser un outil (comme sed) dans ma construction pour remplacer « - AVEC CRYPTAGE ». Avec « WITH ENCRYPTION » sur les fichiers avant de les envoyer à installer
Pour une solution pure SQL, vous pouvez utiliser REPLACE.
Autres conseils
Vous pouvez consulter Encrypting toutes les procédures stockées d'une base de données :
Si vous décidez que vous devez protéger votre SQL Stored Procédures, et la pensée encryptage était une bonne idée, être très prudent !!! Base de données des procédures stockées Crypter les NE DOIVENT PAS se faire sans ayant des fichiers de sauvegarde ou une sorte de contrôle de code source pour le stockées procédures. La raison pour laquelle je dis cela parce que, une fois qu'ils sont cryptés, il n'y a pas tournant autour. (Oui, il existe des outils tiers qui déchiffrera votre code, mais pourquoi passer par ce problème.)
Cette astuce est quelque chose que je développé parce que mon entreprise avait besoin pour héberger l'application sur un serveur différent, et nous étions concernés notre code étant compromise. Donc, pour livrer la base de données, nous a décidé de chiffrer toutes les procédures stockées. Avoir plus d'une centaine procédures écrites, je ne voulais pas ouvrir chaque procédure et coller « AVEC CRYPTAGE » dans chaque procédure stockée. (Pour ceux vous qui ne savent pas comment chiffrer, reportez-vous Comment puis-je protéger mon Stored Code de procédure [^]). J'ai donc décidé de faire mon propre petit C # application qui fait la même chose.
Cette application est une application console faite en utilisant Visual Studio 2005 et SQL Server 2005. Les paramètres d'entrée sont nom de base de données, l'adresse du serveur, nom d'utilisateur et mot de passe de la base de données. Une fois que vous êtes en mesure de fournir ces détails, vous êtes prêt à avoir tous vos procédures stockées cryptées.
J'ai mis le code de ma demande ici est. Pour que ce code fonctionne, vous devrez ajouter un « Microsft.SQlserver.SMO » refrence à l'application, de sorte que la classes telles que "base de données" et "StoredProcedure" sont accessibles.
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;
}
}
}
WITH ENCRYPTION
signifie que le code derrière le proc ne sont pas stockées dans la table syscomments.
Vous pouvez écrire un script qui fait un exec sp_helptext 'MyProcName'
et obtient le contenu dans un VARCHAR (MAX) il peut contenir des procédures multilignes / grandes facilement et modifiy la procédure à partir de son état d'origine
CREATE MyProcName AS
SELECT SecretColumns From TopSecretTable
changement CREATE
à ALTER
et AS
entouré d'espace ou une tabulation ou saut de ligne (bon endroit pour utiliser les expressions régulières) à WITH ENCRYPTION AS
ALTER MyProcName WITH ENCRYPTION AS
SELECT SecretColumns From TopSecretTable
Cela permet de masquer tout le code pour la procédure stockée sur le serveur de production.
Vous pouvez mettre ceci dans un LOOP
ou un CURSOR
(pas vraiment une IMHO opération à base de jeu) pour tous les objets d'un type particulier et / ou convention de nommage que vous souhaitez chiffrer, et l'exécuter à chaque fois que vous déployez.
Je recommande la création de la procédure stockée dans une variable de chaîne multi-ligne, puis d'insérer ou de modifier à l'aide sp_executesql
. Le seul inconvénient gênant de cette approche est doublement des guillemets simples pour les chaînes.
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;
[Notez les espaces autour des variables.]
Tous mes scripts utiliser cette méthode, qui fonctionne bien une fois que vous vous habituez à la citation chose doubler.
J'utilise aussi un fichier batch pour appeler le script et les variables de ligne de commande SQLCMD-mode pour sélectionner divers comportements, ce qui le rend reproductible et facile à tester.
Utilisez cette requête qui Chiffrer toutes les procédures de base de données
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
J'ai écrit un curseur, par étapes et encrypte la plupart des objets.
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
1) exporter Créer un code pour SP et les fonctions. Gardez-le sauvegardé. par exemple D: \ SP2.sql "
2) ce transact code SQL, générer le script existant supprimer sP et fonctions
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) Ce code Poweshell remplacer
AS
BEGIN
par
WITH ENCRYPTION
AS
BEGIN
Le code
$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
Ce serait plus rapide avec un .replace (,), mais le problème avec la fin des lignes ...
4) exécuter le SP-WithEncrypt.sql dans SSMS
Je l'ai fait une mise à jour à l'une des réponses ci-dessus en supprimant la dépendance à la première balise de début. J'ai eu une situation où pas toutes mes procédures stockées avaient BEGIN et END.
I utilisé à la place de la clause AS et également utilisé un cas la version sensible du charindex (en ajoutant une collation)
Son pas une solution parfaite, mais aidé à obtenir plus de mes procédures stockées cryptées.
Voici mon code mis à jour:
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