Cómo averiguar quién eliminó algunos datos de SQL Server


29

Mi jefe recibió ayer una consulta de un cliente preguntando cómo podrían averiguar quién eliminó algunos datos en su base de datos de SQL Server (si es importante, es la edición express).

Pensé que esto podría encontrarse en el registro de transacciones (siempre que no se haya truncado). ¿Es esto correcto? Y si es así, ¿cómo haces para encontrar esta información?

Respuestas:


35

No he probado fn_dblog en Express, pero si está disponible, lo siguiente le dará operaciones de eliminación:

SELECT 
    * 
FROM 
    fn_dblog(NULL, NULL) 
WHERE 
    Operation = 'LOP_DELETE_ROWS'

Tome la identificación de la transacción para las transacciones que le interesan e identifique el SID que inició la transacción con:

SELECT
    [Transaction SID]
FROM
    fn_dblog(NULL, NULL)
WHERE
    [Transaction ID] = @TranID
AND
    [Operation] = 'LOP_BEGIN_XACT'

Luego identifique al usuario desde el SID:

SELECT
    *
FROM 
    sysusers
WHERE
    [sid] = @SID

Editar: reunir todo eso para encontrar eliminaciones en una tabla específica:

DECLARE @TableName sysname
SET @TableName = 'dbo.Table_1'

SELECT
    u.[name] AS UserName
    , l.[Begin Time] AS TransactionStartTime
FROM
    fn_dblog(NULL, NULL) l
INNER JOIN
    (
    SELECT
        [Transaction ID]
    FROM 
        fn_dblog(NULL, NULL) 
    WHERE
        AllocUnitName LIKE @TableName + '%'
    AND
        Operation = 'LOP_DELETE_ROWS'
    ) deletes
ON  deletes.[Transaction ID] = l.[Transaction ID]
INNER JOIN
    sysusers u
ON  u.[sid] = l.[Transaction SID]

De hecho, esto funciona con SQL express, pero en mi sistema solo muestra las transacciones que ocurrieron hoy. ¿No pensé que SQL Express tenía un truncamiento del registro de transacciones fuera de la caja?
Matt Wilko

55
Si su base de datos está en un modelo de recuperación simple, no puede hacer suposiciones sobre cuánto tiempo las transacciones inactivas permanecerán en el registro.
Aaron Bertrand

3
El registro de transacciones es fundamental, más que opcional. ¿Cuál es el modelo de recuperación para la base de datos (simple o completa) y cómo se configuran las copias de seguridad (solo completa o registro de respaldo + completa)?
Mark Storey-Smith

Robé esto para mi respuesta aquí, aunque se reestructuró un poco para evitar que se uniera fn_dblog. Una desventaja es que devuelve la base de datos en USERNAME()lugar del nombre de inicio de sesión mucho más útil.
Martin Smith

3

Si la base de datos está en modo de recuperación completa o si tiene copias de seguridad del registro de transacciones, puede intentar leerlas utilizando lectores de registro de terceros.

Puede probar ApexSQL Log (premium pero tiene una prueba gratuita) o SQL Log Rescue (gratis pero solo SQL 2000).


3

cómo podrían averiguar quién eliminó algunos datos en su base de datos de SQL Server

Aunque esto se responde, quería agregar que SQL Server tiene habilitado un rastreo predeterminado y se puede utilizar para averiguar quién dejó / modificó los objetos.

Eventos de objeto

Los eventos de objeto incluyen: Objeto alterado, Objeto creado y Objeto eliminado

nota: SQL Server por defecto tiene 5 archivos de rastreo, 20 MB cada uno y no se conoce ningún método compatible para cambiar esto. Si tiene un sistema ocupado, los archivos de rastreo pueden pasar demasiado rápido (incluso en cuestión de horas) y es posible que no pueda detectar algunos de los cambios.

Se puede encontrar un excelente ejemplo: el rastreo predeterminado en SQL Server: el poder del rendimiento y la auditoría de seguridad


1

Puede intentar este procedimiento para consultar los archivos de respaldo de registro y encontrar en qué archivo (s) de respaldo de registro todavía estaba / último presente un valor específico de una columna de una tabla.

Para encontrar al usuario, después de encontrar en qué respaldo de registro existía el último valor, puede restaurar una base de datos hasta ese respaldo de respaldo y luego seguir la respuesta de Mark Storey-Smith .

Algunos requisitos previos

  • saber qué valores de qué columnas se eliminaron
  • Están bajo el modelo de recuperación completa y están realizando copias de seguridad de registros
  • tiene fechas o identificadores en sus copias de seguridad de registros, como cuando usa la solución de Ola Hallengren

Renuncia

Esta solución está lejos de ser resistente al agua, y se necesita mucho más trabajo.

No se ha probado en entornos a gran escala, o incluso en cualquier entorno aparte de unas pocas pruebas pequeñas. La ejecución actual fue en SQL Server 2017.

Puede usar el siguiente procedimiento de Muhammad Imran que modifiqué para trabajar con el contenido de las copias de seguridad de registros en lugar de los contenidos del registro de una base de datos en vivo.

De esta manera, técnicamente no está haciendo restauraciones, sino que está volcando el contenido del registro en una tabla temporal. Probablemente seguirá siendo lento y está muy abierto a errores y problemas. Pero podría funcionar, en teoría ™.

El procedimiento almacenado utiliza la fn_dump_dblogfunción no documentada para leer los archivos de registro.


Entorno de prueba

Considere esta base de datos, donde insertamos algunas filas, tomamos 2 copias de seguridad de registro, y en la tercera copia de seguridad de registro eliminamos todas las filas.

CREATE DATABASE WrongDeletesDatabase
GO
USE WrongDeletesDatabase
GO
BACKUP DATABASE WrongDeletesDatabase TO DISK ='c:\temp\Full.bak'

ALTER DATABASE WrongDeletesDatabase SET RECOVERY FULL
GO

CREATE TABLE dbo.WrongDeletes(ID INT, val varchar(255))

INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (1,'value1')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (2,'value2')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log2.trn'
GO
DELETE FROM dbo.WrongDeletes
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log3.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (3,'value3')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log4.trn'
GO

El procedimiento

Puede encontrar y descargar el procedimiento almacenado aquí .

No podría agregarlo aquí, ya que es más grande que el límite de caracteres, y haría que esta respuesta sea aún menos clara de lo que es.

Aparte de esto, debería poder ejecutar el procedimiento.

Ejecutando el procedimiento

Un ejemplo de esto, cuando agrego todos mis archivos de registro ( 4) al procedimiento almacenado y ejecuto el procedimiento buscando valor1

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes', 
                                    @SearchString = 'value1', 
                                    @SearchColumn = 'val',
                                    @LogBackupFolder ='C:\temp\Logs\'

Esto me atrapa:

ID  val LogFileName
1   value1  c:\temp\Logs\log3.trn
1   value1  c:\temp\Logs\log1.trn

Donde podemos encontrar cuándo fue la última vez que value1ocurrió una operación , la eliminación en log3.trn.

Algunos datos de prueba más, agregando una tabla con diferentes columnas

CREATE TABLE dbo.WrongDeletes2(Wow varchar(255), Anotherval varchar(255),Val3 int)

INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (1,'value1')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('b','value1',1)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log1_1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (2,'value2')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('c','value2',2)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log2_1.trn'
GO
DELETE FROM dbo.WrongDeletes
DELETE FROM dbo.WrongDeletes2
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log3_1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (3,'value3')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('d','value3',3)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log4_1.trn'
GO

Cambiar los nombres de los archivos de registro y volver a ejecutar el procedimiento

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes', 
                                    @SearchString = 'value1', 
                                    @SearchColumn = 'val',
                                    @LogBackupFolder ='C:\temp\Logs\'

Resultado

ID  val LogFileName
1   value1  c:\temp\Logs\log1_1.trn
1   value1  c:\temp\Logs\log3_1.trn
1   value1  c:\temp\Logs\log3_1.trn

Una nueva ejecución, buscando el entero ( 2) en la val3columna dedbo.WrongDeletes2

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes2', 
                                    @SearchString = '2', 
                                    @SearchColumn = 'Val3',
                                    @LogBackupFolder ='C:\temp\Logs\'

Resultado

Anotherval  Val3    Wow LogFileName
value2  2   c   c:\temp\Logs\log2.trn
value2  2   c   c:\temp\Logs\log3.trn

Aplicando la respuesta de Mark Storey-Smith

Ahora sabemos que sucedió en el tercer archivo de registro, restauremos hasta ese punto:

USE master
GO
ALTER DATABASE WrongDeletesDatabase SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
ALTER DATABASE WrongDeletesDatabase SET ONLINE 
GO
RESTORE DATABASE WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\Full.bak' WITH NORECOVERY,REPLACE
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log1.trn' WITH NORECOVERY
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log2.trn' WITH NORECOVERY
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log3.trn' WITH RECOVERY
GO
USE WrongDeletesDatabase
GO

Ejecutando la última consulta en su respuesta

SELECT
    u.[name] AS UserName
    , l.[Begin Time] AS TransactionStartTime
FROM
    fn_dblog(NULL, NULL) l
INNER JOIN
    (
    SELECT
        [Transaction ID]
    FROM 
        fn_dblog(NULL, NULL) 
    WHERE
        AllocUnitName LIKE @TableName + '%'
    AND
        Operation = 'LOP_DELETE_ROWS'
    ) deletes
ON  deletes.[Transaction ID] = l.[Transaction ID]
INNER JOIN
    sysusers u
ON  u.[sid] = l.[Transaction SID]

Resultado para mí (administrador de sistemas)

UserName    TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.