
Existem alguns objetos nos databases SQL Server que podem ser encriptados a fim de proteger a sua lógica de negócios:
- procedures;
- views;
- functions;
- triggers.
Quando criamos um desses objetos podemos usar a cláusula WITH ENCRYPTION, que aplica criptografia ao conteúdo do objeto. Ele continua funcionando normalmente, mas não pode ter o código lido.
CREATE DATABASE EXEMPLO
GO
USE EXEMPLO
GO
CREATE PROC USP_PROCEDURE
@VALOR VARCHAR(10)
AS
SELECT CONVERT(VARCHAR, GETDATE(), 103) AS 'DATA_ATUAL',
CONVERT(VARCHAR, GETDATE(), 108) AS 'HORA_ATUAL',
UPPER(@VALOR) AS 'CONTEUDO_DE_VALOR'
GO
EXEC SP_HELP USP_PROCEDURE
/*
Name Owner Type Created_datetime
-------------- ------ ----------------- ------------------------
USP_PROCEDURE dbo stored procedure 2010-05-19 07:29:29.150
Parameter_name Type Length Prec Scale Param_order Collation
--------------- -------- ------ ----- ------ ----------- ---------------------------
@VALOR varchar 10 10 NULL 1 Latin1_General_CI_AS_KS_WS
*/
EXEC SP_HELPTEXT USP_PROCEDURE
/*
Text
------------------------------------------------------------------
CREATE PROC USP_PROCEDURE
@VALOR VARCHAR(10)
AS
SELECT CONVERT(VARCHAR, GETDATE(), 103) AS 'DATA_ATUAL',
CONVERT(VARCHAR, GETDATE(), 108) AS 'HORA_ATUAL',
UPPER(@VALOR) AS 'CONTEUDO_DE_VALOR'
*/
EXEC USP_PROCEDURE 'algo'
/*
DATA_ATUAL HORA_ATUAL CONTEUDO_DE_VALOR
----------- ----------- -----------------
19/05/2010 07:29:30 ALGO
*/
GO
ALTER PROC USP_PROCEDURE
@VALOR VARCHAR(10)
WITH ENCRYPTION
AS
SELECT CONVERT(VARCHAR, GETDATE(), 103) AS 'DATA_ATUAL',
CONVERT(VARCHAR, GETDATE(), 108) AS 'HORA_ATUAL',
UPPER(@VALOR) AS 'CONTEUDO_DE_VALOR'
GO
EXEC SP_HELP USP_PROCEDURE
/*
Name Owner Type Created_datetime
-------------- ------ ----------------- ------------------------
USP_PROCEDURE dbo stored procedure 2010-05-19 07:29:29.150
Parameter_name Type Length Prec Scale Param_order Collation
--------------- -------- ------ ----- ------ ----------- ---------------------------
@VALOR varchar 10 10 NULL 1 Latin1_General_CI_AS_KS_WS
*/
EXEC SP_HELPTEXT USP_PROCEDURE
/*
The object comments have been encrypted.
*/
EXEC USP_PROCEDURE 'algo'
/*
DATA_ATUAL HORA_ATUAL CONTEUDO_DE_VALOR
----------- ----------- -----------------
19/05/2010 07:29:57 ALGO
*/
Você pode observar que a procedure sp_help sempre traz o mesmo conteúdo e a execução também não é prejudicada pela criptografia. Claro... senão não teria nenhum sentido criptografar um objeto...
Somente o sp_helptext é que tem o comportamento alterado: para objetos criptografados ele traz uma mensagem "o objeto está criptografado".
Como existem sutis diferenças entre a segurança do SQL Server 2000, 2005 e 2008, vou começar explicando como funciona no SQL Server 2000. Depois, com tempo, vou acrescentando material para as novas versões.
O algoritmo usado nessa criptografia é o XOR (eXclusive OR - ou exclusivo), que trabalha com a informação bit-a-bit, comparando dois valores e retornando o valor 1 se eles forem diferentes (exclusivos) ou 0 se eles forem iguais.
Por exemplo:
select 6 ^ 4 as '6 XOR 4'
/*
6 XOR 4
-----------
2
*/
Na realidade, o que acontece é que o algoritmo XOR decompõe o número 6 e o número 4 em binário e trabalha com eles da seguinte forma:
6 -> 00000000 00000000 00000000 00000110
4 -> 00000000 00000000 00000000 00000100
-----------------------------------
6^4 -> 00000000 00000000 00000000 00000010 -> 2
E o mais interessante é que o XOR é simétrico, isto é: a mesma chave usada para criptografar a mensagem é usada para decriptar. Isso quer dizer que se eu aplicar a chave à mensagem criptografada obtenho a mensagem original:
select 2 ^ 4 as '2 XOR 4'
/*
2 XOR 4
-----------
6
*/
2 -> 00000000 00000000 00000000 00000010
4 -> 00000000 00000000 00000000 00000100
-----------------------------------
2^4 -> 00000000 00000000 00000000 00000110 -> 6
Para deixar claro, quando estudamos criptografia, vemos os seguintes termos:
- Mensagem - o texto ou valor original, que pode ser lido sem problemas (o valor 6, no exemplo acima);
- chave - uma sequência de bits usado para criptografar a mensagem (o valor 4, no exemplo acima);
- algoritmo - sequência de passos que são realizados para criptografar a mensagem, usando a chave;
- mensagem cifrada - a mensagem original depois de aplicado o algoritmo (o valor 2, no exemplo acima);
- emissor - quem envia a mensagem;
- receptor - quem recebe a mensagem;
- criptografia - ato de criptografar (alterar a mensagem usando a chave);
- decriptação - ato de decriptar (obter a mensagem a partir da mensagem cifrada e chave).
E, voltando ao SQL Server, temos que os objetos do banco (os quatro citados anteriormente) tem suas propriedades armazenadas nas seguintes tabelas (existem outras, mas não vem ao caso:
- sysobjects - definição do objeto;
- syscolumns - definição das colunas do objeto (ou argumentos, no caso de procedures e functions);
- syscomments - corpo do objeto (lógica).
Dentro da syscomments temos duas colunas que nos interessa:
- text - o código, em Unicode;
- ctext - o código transformado em binário.
SELECT ID,
CTEXT,
TEXT
FROM SYSCOMMENTS
WHERE ID = OBJECT_ID('USP_PROCEDURE')
/*
--com o objeto sem criptografia
ID CTEXT TEXT
----------- ----------------- -----------------------------
1977058079 0x435245415445... CREATE PROC USP_PROCEDURE...
--com o objeto criptografado
ID CTEXT TEXT
----------- ----------------- --------------------
1977058079 0xF879595BA43... ????????????????...
*/
Com isso em mente, podemos imaginar o seguinte: se eu criptografar um texto qualquer e fizer um XOR com o texto original, tenho a chave (no exemplo acima, eu faria 6 ^ 4 = 2, onde 2 é a chave)... aí, com a chave, consigo obter o original de novo!!!
E o código ficou assim:
CREATE PROCEDURE USP_DECRIPTA_OBJETO
@NOME_OBJETO VARCHAR(50)
AS
SET ANSI_WARNINGS OFF
DECLARE @CTEXT_ORIGINAL NVARCHAR(4000),
@COMANDO NVARCHAR(4000),
@CTEXT_NOVO NVARCHAR(4000),
@COMANDO_NOVO NVARCHAR(4000),
@CONTADOR INT
SELECT @CTEXT_ORIGINAL = CTEXT
FROM SYSCOMMENTS
WHERE ID = OBJECT_ID(@NOME_OBJETO)
SET @COMANDO = 'ALTER PROCEDURE '
+ @NOME_OBJETO
+ ' WITH ENCRYPTION AS '
+ REPLICATE('-', 4000 - 62)
EXECUTE (@COMANDO)
SELECT @CTEXT_NOVO = CTEXT
FROM SYSCOMMENTS
WHERE ID = OBJECT_ID(@NOME_OBJETO)
SET @COMANDO='CREATE PROCEDURE '
+ @NOME_OBJETO
+ ' WITH ENCRYPTION AS '
+ REPLICATE('-', 4000 - 62)
SET @CONTADOR = 1
SET @COMANDO_NOVO = REPLICATE(N'A',
(DATALENGTH(@CTEXT_ORIGINAL) / 2))
WHILE @CONTADOR <= DATALENGTH(@CTEXT_ORIGINAL)/2
BEGIN
SET @COMANDO_NOVO = STUFF(@COMANDO_NOVO, @CONTADOR, 1,
NCHAR(UNICODE(SUBSTRING(@CTEXT_ORIGINAL, @CONTADOR, 1)) ^
(UNICODE(SUBSTRING(@COMANDO, @CONTADOR, 1)) ^
UNICODE(SUBSTRING(@CTEXT_NOVO, @CONTADOR, 1)))))
SET @CONTADOR=@CONTADOR+1
END
EXECUTE('DROP PROCEDURE ' + @NOME_OBJETO)
IF CHARINDEX('WITH ENCRYPTION', UPPER(@COMANDO_NOVO)) > 0
SET @COMANDO_NOVO = REPLACE(UPPER(@COMANDO_NOVO), 'WITH ENCRYPTION', '')
EXECUTE(@COMANDO_NOVO)
GO
Agora, sim!!! é só descompilar as procedures e ver o seu conteúdo...
OBS.: eu já tinha postado isso em 2006, no site da DevMedia, mas como alguém tocou nesse assunto esses dias atrás, achei adequado refazer o post.